From 3fc3905a9168f38e7ab2a9a75e10f121a5a083a4 Mon Sep 17 00:00:00 2001 From: Petr Javorik Date: Fri, 15 Jun 2018 14:12:22 +0200 Subject: [PATCH 01/43] Set theme jekyll-theme-cayman --- _config.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 _config.yml diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..c419263 --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file From dc5d17cfeae0ef24d3664bce2df2e6aaf557b513 Mon Sep 17 00:00:00 2001 From: Maple Date: Fri, 22 Jun 2018 10:02:44 +0200 Subject: [PATCH 02/43] namespace wrap --- example.cpp | 2 + include/BitfinexAPI.hpp | 1741 ++++++++++++++++++++------------------- 2 files changed, 873 insertions(+), 870 deletions(-) diff --git a/example.cpp b/example.cpp index 5394ead..737339c 100644 --- a/example.cpp +++ b/example.cpp @@ -18,6 +18,8 @@ using std::ifstream; using std::endl; using std::string; +using BfxAPI::BitfinexAPI; + int main(int argc, char *argv[]) { diff --git a/include/BitfinexAPI.hpp b/include/BitfinexAPI.hpp index 40b64a6..2bce5c1 100644 --- a/include/BitfinexAPI.hpp +++ b/include/BitfinexAPI.hpp @@ -1,5 +1,3 @@ -////////////////////////////////////////////////////////////////////////////// -// // BitfinexAPI.hpp // // @@ -62,976 +60,979 @@ using CryptoPP::byte; #endif -class BitfinexAPI +namespace BfxAPI { -public: - - ////////////////////////////////////////////////////////////////////////////// - // Enumerations - ////////////////////////////////////////////////////////////////////////////// - enum bfxERR // positive values for curl internal error codes - { - curlERR = -50, - badSymbol = -40, - badCurrency = -39, - badDepositMethod = -38, - badWalletType = -37, - requiredParamsMissing = -36, - wireParamsMissing = -35, - addressParamsMissing = -34, - badOrderType = -33 - }; - - ////////////////////////////////////////////////////////////////////////////// - // Typedefs - ////////////////////////////////////////////////////////////////////////////// - - // Structure for multiple new orders endpoint - struct sOrder + class BitfinexAPI { - string symbol; - double amount; - double price; - string side; - string type; - }; - typedef vector vOrders; - - typedef vector vIds; - - ////////////////////////////////////////////////////////////////////////////// - // Constructor - destructor - ////////////////////////////////////////////////////////////////////////////// - - explicit BitfinexAPI():BitfinexAPI("", "") {} - - explicit BitfinexAPI(const string &accessKey, const string &secretKey): - _accessKey(accessKey), - _secretKey(secretKey), - _WDconfFilePath("doc/withdraw.conf"), - _APIurl("https://api.bitfinex.com/v1"), - _curl(curl_easy_init()) - { - string temp; - getSymbols(temp); - jsonutils::arrayToUset(_symbols, temp); + public: - _currencies = - { - "BTG", - "DSH", - "ETC", - "ETP", - "EUR", - "GBP", - "IOT", - "JPY", - "LTC", - "NEO", - "OMG", - "SAN", - "USD", - "XMR", - "XRP", - "ZEC" - }; - // As found on https://bitfinex.readme.io/v1/reference#rest-auth-deposit - _methods = - { - "bcash" - "bitcoin", - "ethereum", - "ethereumc", - "ethereumc", - "litecoin", - "mastercoin", - "monero", - "tetheruso", - "zcash", - }; - _walletNames = + ////////////////////////////////////////////////////////////////////////////// + // Enumerations + ////////////////////////////////////////////////////////////////////////////// + enum bfxERR // positive values for curl internal error codes { - "trading", "exchange", "deposit" + curlERR = -50, + badSymbol = -40, + badCurrency = -39, + badDepositMethod = -38, + badWalletType = -37, + requiredParamsMissing = -36, + wireParamsMissing = -35, + addressParamsMissing = -34, + badOrderType = -33 }; - // New order endpoint "type" parameter - _types = + + ////////////////////////////////////////////////////////////////////////////// + // Typedefs + ////////////////////////////////////////////////////////////////////////////// + + // Structure for multiple new orders endpoint + struct sOrder { - "market", - "limit", - "stop", - "trailing-stop", - "fill-or-kill", - "exchange market", - "exchange limit", - "exchange stop", - "exchange trailing-stop", - "exchange fill-or-kill" + string symbol; + double amount; + double price; + string side; + string type; }; - } - - ~BitfinexAPI() - { - curl_easy_cleanup(_curl); - } - - ////////////////////////////////////////////////////////////////////////////// - // Accessors - ////////////////////////////////////////////////////////////////////////////// - - string getWDconfFilePath() const { return _WDconfFilePath; } - - void setWDconfFilePath(const string &path) { _WDconfFilePath = path; } - - void setKeys(const string &accessKey, const string &secretKey) - { - this->_accessKey = accessKey; - this->_secretKey = secretKey; - } - - ////////////////////////////////////////////////////////////////////////////// - // Public endpoints - ////////////////////////////////////////////////////////////////////////////// - - int getTicker(string &result, const string &symbol) - { - // Is symbol valid ? - if(!inArray(symbol, _symbols)) + typedef vector vOrders; + + typedef vector vIds; + + ////////////////////////////////////////////////////////////////////////////// + // Constructor - destructor + ////////////////////////////////////////////////////////////////////////////// + + explicit BitfinexAPI():BitfinexAPI("", "") {} + + explicit BitfinexAPI(const string &accessKey, const string &secretKey): + _accessKey(accessKey), + _secretKey(secretKey), + _WDconfFilePath("doc/withdraw.conf"), + _APIurl("https://api.bitfinex.com/v1"), + _curl(curl_easy_init()) { - return badSymbol; + string temp; + getSymbols(temp); + jsonutils::arrayToUset(_symbols, temp); + + _currencies = + { + "BTG", + "DSH", + "ETC", + "ETP", + "EUR", + "GBP", + "IOT", + "JPY", + "LTC", + "NEO", + "OMG", + "SAN", + "USD", + "XMR", + "XRP", + "ZEC" + }; + // As found on https://bitfinex.readme.io/v1/reference#rest-auth-deposit + _methods = + { + "bcash" + "bitcoin", + "ethereum", + "ethereumc", + "ethereumc", + "litecoin", + "mastercoin", + "monero", + "tetheruso", + "zcash", + }; + _walletNames = + { + "trading", "exchange", "deposit" + }; + // New order endpoint "type" parameter + _types = + { + "market", + "limit", + "stop", + "trailing-stop", + "fill-or-kill", + "exchange market", + "exchange limit", + "exchange stop", + "exchange trailing-stop", + "exchange fill-or-kill" + }; } - return DoGETrequest("/pubticker/" + symbol, "", result); - }; - - int getStats(string &result, const string &symbol) - { - // Is symbol valid ? - if(!inArray(symbol, _symbols)) + ~BitfinexAPI() { - return badSymbol; + curl_easy_cleanup(_curl); } - return DoGETrequest("/stats/" + symbol, "", result); - }; - - int getFundingBook(string &result, - const string ¤cy, - const int &limit_bids = 50, - const int &limit_asks = 50) - { - // Is currency valid ? - if(!inArray(currency, _currencies)) + ////////////////////////////////////////////////////////////////////////////// + // Accessors + ////////////////////////////////////////////////////////////////////////////// + + string getWDconfFilePath() const { return _WDconfFilePath; } + + void setWDconfFilePath(const string &path) { _WDconfFilePath = path; } + + void setKeys(const string &accessKey, const string &secretKey) { - return badCurrency; + this->_accessKey = accessKey; + this->_secretKey = secretKey; } - string params = - "?limit_bids=" + to_string(limit_bids) + - "&limit_asks=" + to_string(limit_asks); - return DoGETrequest("/lendbook/" + currency, params, result); - }; - - int getOrderBook(string &result, - const string &symbol, - const int &limit_bids = 50, - const int &limit_asks = 50, - const bool &group = 1) - { - // Is symbol valid ? - if(!inArray(symbol, _symbols)) + ////////////////////////////////////////////////////////////////////////////// + // Public endpoints + ////////////////////////////////////////////////////////////////////////////// + + int getTicker(string &result, const string &symbol) { - return badSymbol; - } + // Is symbol valid ? + if(!inArray(symbol, _symbols)) + { + return badSymbol; + } + + return DoGETrequest("/pubticker/" + symbol, "", result); + }; - string params = - "?limit_bids=" + to_string(limit_bids) + - "&limit_asks=" + to_string(limit_asks) + - "&group=" + to_string(group); - return DoGETrequest("/book/" + symbol, params, result); - }; - - int getTrades(string &result, - const string &symbol, - const time_t &since = 0, - const int &limit_trades = 50) - { - // Is symbol valid ? - if(!inArray(symbol, _symbols)) + int getStats(string &result, const string &symbol) { - return badSymbol; - } + // Is symbol valid ? + if(!inArray(symbol, _symbols)) + { + return badSymbol; + } + + return DoGETrequest("/stats/" + symbol, "", result); + }; - string params = - "?timestamp=" + to_string(since) + - "&limit_trades=" + to_string(limit_trades); - return DoGETrequest("/trades/" + symbol, params, result); - }; - - int getLends(string &result, - const string ¤cy, - const time_t &since = 0, - const int &limit_lends = 50) - { - // Is currency valid ? - if(!inArray(currency, _currencies)) + int getFundingBook(string &result, + const string ¤cy, + const int &limit_bids = 50, + const int &limit_asks = 50) { - return badCurrency; - } + // Is currency valid ? + if(!inArray(currency, _currencies)) + { + return badCurrency; + } + + string params = + "?limit_bids=" + to_string(limit_bids) + + "&limit_asks=" + to_string(limit_asks); + return DoGETrequest("/lendbook/" + currency, params, result); + }; - string params = - "?timestamp=" + to_string(since) + - "&limit_lends=" + to_string(limit_lends); - return DoGETrequest("/lends/" + currency, params, result); - }; - - int getSymbols(string &result) - { - return DoGETrequest("/symbols/", "", result); - }; - - int getSymbolDetails(string &result) - { - return DoGETrequest("/symbols_details/", "", result); - }; - - ////////////////////////////////////////////////////////////////////////////// - // Authenticated endpoints - ////////////////////////////////////////////////////////////////////////////// - - // Account - int getAccountInfo(string &result) - { - string params = "{\"request\":\"/v1/account_infos\",\"nonce\":\"" + getTonce() + "\""; - params += "}"; - return DoPOSTrequest("/account_infos/", params, result); - }; - - int getAccountFees(string &result) - { - string params = "{\"request\":\"/v1/account_fees\",\"nonce\":\"" + getTonce() + "\""; - params += "}"; - return DoPOSTrequest("/account_fees/", params, result); - }; - - int getSummary(string &result) - { - string params = "{\"request\":\"/v1/summary\",\"nonce\":\"" + getTonce() + "\""; - params += "}"; - return DoPOSTrequest("/summary/", params, result); - }; - - int deposit(string &result, - const string &method, - const string &walletName, - const bool &renew = 0) - { - // Is deposit method valid ? - if(!inArray(method, _methods)) + int getOrderBook(string &result, + const string &symbol, + const int &limit_bids = 50, + const int &limit_asks = 50, + const bool &group = 1) { - return badDepositMethod; - } - // Is walletType valid ? - if(!inArray(walletName, _walletNames)) + // Is symbol valid ? + if(!inArray(symbol, _symbols)) + { + return badSymbol; + } + + string params = + "?limit_bids=" + to_string(limit_bids) + + "&limit_asks=" + to_string(limit_asks) + + "&group=" + to_string(group); + return DoGETrequest("/book/" + symbol, params, result); + }; + + int getTrades(string &result, + const string &symbol, + const time_t &since = 0, + const int &limit_trades = 50) { - return badWalletType; - } + // Is symbol valid ? + if(!inArray(symbol, _symbols)) + { + return badSymbol; + } + + string params = + "?timestamp=" + to_string(since) + + "&limit_trades=" + to_string(limit_trades); + return DoGETrequest("/trades/" + symbol, params, result); + }; - string params = "{\"request\":\"/v1/deposit/new\",\"nonce\":\"" + getTonce() + "\""; - params += ",\"method\":\"" + method + "\""; - params += ",\"wallet_name\":\"" + walletName + "\""; - params += ",\"renew\":" + to_string(renew); - params += "}"; - return DoPOSTrequest("/deposit/new/", params, result); - }; - - int getKeyPermissions(string &result) - { - string params = "{\"request\":\"/v1/key_info\",\"nonce\":\"" + getTonce() + "\""; - params += "}"; - return DoPOSTrequest("/key_info/", params, result); - }; - - int getMarginInfos(string &result) - { - string params = "{\"request\":\"/v1/margin_infos\",\"nonce\":\"" + getTonce() + "\""; - params += "}"; - return DoPOSTrequest("/margin_infos/", params, result); - }; - - int getBalances(string &result) - { - string params = "{\"request\":\"/v1/balances\",\"nonce\":\"" + getTonce() + "\""; - params += "}"; - return DoPOSTrequest("/balances/", params, result); - }; - - int transfer(string &result, - const double &amount, - const string ¤cy, - const string &walletfrom, - const string &walletto) - { - // Is currency valid ? - if(!inArray(currency, _currencies)) + int getLends(string &result, + const string ¤cy, + const time_t &since = 0, + const int &limit_lends = 50) { - return badCurrency; - } - // Is walletType valid ? - if(!inArray(walletfrom, _walletNames) || !inArray(walletto, _walletNames)) + // Is currency valid ? + if(!inArray(currency, _currencies)) + { + return badCurrency; + } + + string params = + "?timestamp=" + to_string(since) + + "&limit_lends=" + to_string(limit_lends); + return DoGETrequest("/lends/" + currency, params, result); + }; + + int getSymbols(string &result) { - return badWalletType; - } + return DoGETrequest("/symbols/", "", result); + }; - string params = "{\"request\":\"/v1/transfer\",\"nonce\":\"" + getTonce() + "\""; - params += ",\"amount\":\"" + to_string(amount) + "\""; - params += ",\"currency\":\"" + currency + "\""; - params += ",\"walletfrom\":\"" + walletfrom + "\""; - params += ",\"walletto\":\"" + walletto + "\""; - params += "}"; - return DoPOSTrequest("/transfer/", params, result); - }; - - int withdraw(string &result) // configure withdraw.conf file before use - { - string params = "{\"request\":\"/v1/withdraw\",\"nonce\":\"" + getTonce() + "\""; + int getSymbolDetails(string &result) + { + return DoGETrequest("/symbols_details/", "", result); + }; - // Add params from withdraw.conf - int err = parseWDconfParams(params); - if (err != 0) { return err; }; + ////////////////////////////////////////////////////////////////////////////// + // Authenticated endpoints + ////////////////////////////////////////////////////////////////////////////// - params += "}"; - return DoPOSTrequest("/withdraw/", params, result); - }; - - // Orders - int newOrder(string &result, - const string &symbol, - const double &amount, - const double &price, - const string &side, - const string &type, - const bool &is_hidden = 0, - const bool &is_postonly = 0, - const bool &use_all_available = 0, - const bool &ocoorder = 0, - const double &buy_price_oco = 0) - { - // Is symbol valid ? - if(!inArray(symbol, _symbols)) + // Account + int getAccountInfo(string &result) { - return badSymbol; - } - // Is order type valid ? - if(!inArray(type, _types)) - { - return badOrderType; - } + string params = "{\"request\":\"/v1/account_infos\",\"nonce\":\"" + getTonce() + "\""; + params += "}"; + return DoPOSTrequest("/account_infos/", params, result); + }; - string params = "{\"request\":\"/v1/order/new\",\"nonce\":\"" + getTonce() + "\""; - params += ",\"symbol\":\"" + symbol + "\""; - params += ",\"amount\":\"" + to_string(amount) + "\""; - params += ",\"price\":\"" + to_string(price) + "\""; - params += ",\"side\":\"" + side + "\""; - params += ",\"type\":\"" + type + "\""; - params += ",\"is_hidden\":" + bool2string(is_hidden); - params += ",\"is_postonly\":" + bool2string(is_postonly); - params += ",\"use_all_available\":" + bool2string(use_all_available); - params += ",\"ocoorder\":" + bool2string(ocoorder); - params += ",\"buy_price_oco\":" + bool2string(buy_price_oco); - params += "}"; - return DoPOSTrequest("/order/new/", params, result); - }; - - int newOrders(string &result, const vOrders &orders) - { - string params = "{\"request\":\"/v1/order/new/multi\",\"nonce\":\"" + getTonce() + "\""; + int getAccountFees(string &result) + { + string params = "{\"request\":\"/v1/account_fees\",\"nonce\":\"" + getTonce() + "\""; + params += "}"; + return DoPOSTrequest("/account_fees/", params, result); + }; - // Get pointer to last element in orders. We will not place - // ',' character at the end of the last loop. - auto &last = *(--orders.cend()); + int getSummary(string &result) + { + string params = "{\"request\":\"/v1/summary\",\"nonce\":\"" + getTonce() + "\""; + params += "}"; + return DoPOSTrequest("/summary/", params, result); + }; - params += ",\"payload\":["; - for (const auto &order : orders) + int deposit(string &result, + const string &method, + const string &walletName, + const bool &renew = 0) { - params += "{\"symbol\":\"" + order.symbol + "\""; - params += ",\"amount\":\"" + to_string(order.amount) + "\""; - params += ",\"price\":\"" + to_string(order.price) + "\""; - params += ",\"side\":\"" + order.side + "\""; - params += ",\"type\":\"" + order.type + "\"}"; - if (&order != &last) + // Is deposit method valid ? + if(!inArray(method, _methods)) { - params += ","; + return badDepositMethod; } - } - params += "]"; - params += "}"; - return DoPOSTrequest("/order/new/multi/", params, result); - }; - - int cancelOrder(string &result, const long long &order_id) - { - string params = "{\"request\":\"/v1/order/cancel\",\"nonce\":\"" + getTonce() + "\""; - params += ",\"order_id\":" + to_string(order_id); - params += "}"; - return DoPOSTrequest("/order/cancel/", params, result); - }; - - int cancelOrders(string &result, const vIds &vOrder_ids) - { - string params = "{\"request\":\"/v1/order/cancel/multi\",\"nonce\":\"" + getTonce() + "\""; + // Is walletType valid ? + if(!inArray(walletName, _walletNames)) + { + return badWalletType; + } + + string params = "{\"request\":\"/v1/deposit/new\",\"nonce\":\"" + getTonce() + "\""; + params += ",\"method\":\"" + method + "\""; + params += ",\"wallet_name\":\"" + walletName + "\""; + params += ",\"renew\":" + to_string(renew); + params += "}"; + return DoPOSTrequest("/deposit/new/", params, result); + }; - // Get pointer to last element in vOrders. We will not place - // ',' character at the end of the last loop. - auto &last = *(--vOrder_ids.cend()); + int getKeyPermissions(string &result) + { + string params = "{\"request\":\"/v1/key_info\",\"nonce\":\"" + getTonce() + "\""; + params += "}"; + return DoPOSTrequest("/key_info/", params, result); + }; + + int getMarginInfos(string &result) + { + string params = "{\"request\":\"/v1/margin_infos\",\"nonce\":\"" + getTonce() + "\""; + params += "}"; + return DoPOSTrequest("/margin_infos/", params, result); + }; - params += ", \"order_ids\":["; - for (const auto &order_id : vOrder_ids) + int getBalances(string &result) { - params += to_string(order_id); - if (&order_id != &last) + string params = "{\"request\":\"/v1/balances\",\"nonce\":\"" + getTonce() + "\""; + params += "}"; + return DoPOSTrequest("/balances/", params, result); + }; + + int transfer(string &result, + const double &amount, + const string ¤cy, + const string &walletfrom, + const string &walletto) + { + // Is currency valid ? + if(!inArray(currency, _currencies)) { - params += ","; + return badCurrency; } - } - params += "]"; - params += "}"; - return DoPOSTrequest("/order/cancel/multi/", params, result); - }; - - int cancelAllOrders(string &result) - { - string params = "{\"request\":\"/v1/order/cancel/all\",\"nonce\":\"" + getTonce() + "\""; - params += "}"; - return DoPOSTrequest("/order/cancel/all/", params, result); - }; - - int replaceOrder(string &result, - const long long &order_id, + // Is walletType valid ? + if(!inArray(walletfrom, _walletNames) || !inArray(walletto, _walletNames)) + { + return badWalletType; + } + + string params = "{\"request\":\"/v1/transfer\",\"nonce\":\"" + getTonce() + "\""; + params += ",\"amount\":\"" + to_string(amount) + "\""; + params += ",\"currency\":\"" + currency + "\""; + params += ",\"walletfrom\":\"" + walletfrom + "\""; + params += ",\"walletto\":\"" + walletto + "\""; + params += "}"; + return DoPOSTrequest("/transfer/", params, result); + }; + + int withdraw(string &result) // configure withdraw.conf file before use + { + string params = "{\"request\":\"/v1/withdraw\",\"nonce\":\"" + getTonce() + "\""; + + // Add params from withdraw.conf + int err = parseWDconfParams(params); + if (err != 0) { return err; }; + + params += "}"; + return DoPOSTrequest("/withdraw/", params, result); + }; + + // Orders + int newOrder(string &result, const string &symbol, const double &amount, const double &price, const string &side, const string &type, const bool &is_hidden = 0, - const bool &use_remaining = 0) - { - // Is symbol valid ? - if(!inArray(symbol, _symbols)) + const bool &is_postonly = 0, + const bool &use_all_available = 0, + const bool &ocoorder = 0, + const double &buy_price_oco = 0) { - return badSymbol; - } - // Is order type valid ? - if(!inArray(type, _types)) + // Is symbol valid ? + if(!inArray(symbol, _symbols)) + { + return badSymbol; + } + // Is order type valid ? + if(!inArray(type, _types)) + { + return badOrderType; + } + + string params = "{\"request\":\"/v1/order/new\",\"nonce\":\"" + getTonce() + "\""; + params += ",\"symbol\":\"" + symbol + "\""; + params += ",\"amount\":\"" + to_string(amount) + "\""; + params += ",\"price\":\"" + to_string(price) + "\""; + params += ",\"side\":\"" + side + "\""; + params += ",\"type\":\"" + type + "\""; + params += ",\"is_hidden\":" + bool2string(is_hidden); + params += ",\"is_postonly\":" + bool2string(is_postonly); + params += ",\"use_all_available\":" + bool2string(use_all_available); + params += ",\"ocoorder\":" + bool2string(ocoorder); + params += ",\"buy_price_oco\":" + bool2string(buy_price_oco); + params += "}"; + return DoPOSTrequest("/order/new/", params, result); + }; + + int newOrders(string &result, const vOrders &orders) { - return badOrderType; - } + string params = "{\"request\":\"/v1/order/new/multi\",\"nonce\":\"" + getTonce() + "\""; + + // Get pointer to last element in orders. We will not place + // ',' character at the end of the last loop. + auto &last = *(--orders.cend()); + + params += ",\"payload\":["; + for (const auto &order : orders) + { + params += "{\"symbol\":\"" + order.symbol + "\""; + params += ",\"amount\":\"" + to_string(order.amount) + "\""; + params += ",\"price\":\"" + to_string(order.price) + "\""; + params += ",\"side\":\"" + order.side + "\""; + params += ",\"type\":\"" + order.type + "\"}"; + if (&order != &last) + { + params += ","; + } + } + params += "]"; + params += "}"; + return DoPOSTrequest("/order/new/multi/", params, result); + }; - string params = "{\"request\":\"/v1/order/cancel/replace\",\"nonce\":\"" + getTonce() + "\""; - params += ",\"order_id\":" + to_string(order_id); - params += ",\"symbol\":\"" + symbol + "\""; - params += ",\"amount\":\"" + to_string(amount) + "\""; - params += ",\"price\":\"" + to_string(price) + "\""; - params += ",\"side\":\"" + side + "\""; - params += ",\"type\":\"" + type + "\""; - params += ",\"is_hidden\":" + bool2string(is_hidden); - params += ",\"use_all_available\":" + bool2string(use_remaining); - params += "}"; - return DoPOSTrequest("/order/cancel/replace/", params, result); - }; - - int getOrderStatus(string &result, const long long &order_id) - { - string params = "{\"request\":\"/v1/order/status\",\"nonce\":\"" + getTonce() + "\""; - params += ",\"order_id\":" + to_string(order_id); - params += "}"; - return DoPOSTrequest("/order/status/", params, result); - }; - - int getActiveOrders(string &result) - { - string params = "{\"request\":\"/v1/orders\",\"nonce\":\"" + getTonce() + "\""; - params += "}"; - return DoPOSTrequest("/orders/", params, result); - }; - - int getOrdersHistory(string &result, const int &limit = 50) - { - string params = "{\"request\":\"/v1/orders/hist\",\"nonce\":\"" + getTonce() + "\""; - params += ",\"limit\":" + to_string(limit); - params += "}"; - return DoPOSTrequest("/orders/hist/", params, result); - }; - - - // Positions - int getActivePositions(string &result) - { - string params = "{\"request\":\"/v1/positions\",\"nonce\":\"" + getTonce() + "\""; - params += "}"; - return DoPOSTrequest("/positions/", params, result); - }; - - int claimPosition(string &result, - long long &position_id, - const double &amount) - { - string params = "{\"request\":\"/v1/position/claim\",\"nonce\":\"" + getTonce() + "\""; - params += ",\"position_id\":" + to_string(position_id); - params += ",\"amount\":\"" + to_string(amount) + "\""; - params += "}"; - return DoPOSTrequest("/position/claim/", params, result); - }; - - // Historical data - int getBalanceHistory(string &result, - const string ¤cy, - const time_t &since = 0, - const time_t &until = 0, - const int &limit = 500, - const string &walletType = "all") - { - // Is currency valid ? - if(!inArray(currency, _currencies)) + int cancelOrder(string &result, const long long &order_id) { - return badCurrency; - } - // Is wallet type valid ? - // Modified condition which accepts "all" value for all wallets balances together. - if(!inArray(walletType, _walletNames) && walletType != "all") + string params = "{\"request\":\"/v1/order/cancel\",\"nonce\":\"" + getTonce() + "\""; + params += ",\"order_id\":" + to_string(order_id); + params += "}"; + return DoPOSTrequest("/order/cancel/", params, result); + }; + + int cancelOrders(string &result, const vIds &vOrder_ids) { - return badWalletType; - } + string params = "{\"request\":\"/v1/order/cancel/multi\",\"nonce\":\"" + getTonce() + "\""; + + // Get pointer to last element in vOrders. We will not place + // ',' character at the end of the last loop. + auto &last = *(--vOrder_ids.cend()); + + params += ", \"order_ids\":["; + for (const auto &order_id : vOrder_ids) + { + params += to_string(order_id); + if (&order_id != &last) + { + params += ","; + } + } + params += "]"; + params += "}"; + return DoPOSTrequest("/order/cancel/multi/", params, result); + }; - string params = "{\"request\":\"/v1/history\",\"nonce\":\"" + getTonce() + "\""; - params += ",\"currency\":\"" + currency + "\""; - params += ",\"since\":\"" + to_string(since) + "\""; - params += ",\"until\":\"" + (!until ? getTonce() : to_string(until)) + "\""; - params += ",\"limit\":" + to_string(limit); - if (walletType != "all") + int cancelAllOrders(string &result) { - params += ",\"wallet\":\"" + walletType + "\""; - } + string params = "{\"request\":\"/v1/order/cancel/all\",\"nonce\":\"" + getTonce() + "\""; + params += "}"; + return DoPOSTrequest("/order/cancel/all/", params, result); + }; - params += "}"; - return DoPOSTrequest("/history/", params, result); - }; - - int getWithdrawalHistory(string &result, - const string ¤cy, - const string &method = "all", - const time_t &since = 0, - const time_t &until = 0, - const int &limit = 500) - { - // Is currency valid ? - if(!inArray(currency, _currencies)) + int replaceOrder(string &result, + const long long &order_id, + const string &symbol, + const double &amount, + const double &price, + const string &side, + const string &type, + const bool &is_hidden = 0, + const bool &use_remaining = 0) { - return badCurrency; - } - // Is deposit method valid ? - if(!inArray(method, _methods) && method != "wire" && method != "all") + // Is symbol valid ? + if(!inArray(symbol, _symbols)) + { + return badSymbol; + } + // Is order type valid ? + if(!inArray(type, _types)) + { + return badOrderType; + } + + string params = "{\"request\":\"/v1/order/cancel/replace\",\"nonce\":\"" + getTonce() + "\""; + params += ",\"order_id\":" + to_string(order_id); + params += ",\"symbol\":\"" + symbol + "\""; + params += ",\"amount\":\"" + to_string(amount) + "\""; + params += ",\"price\":\"" + to_string(price) + "\""; + params += ",\"side\":\"" + side + "\""; + params += ",\"type\":\"" + type + "\""; + params += ",\"is_hidden\":" + bool2string(is_hidden); + params += ",\"use_all_available\":" + bool2string(use_remaining); + params += "}"; + return DoPOSTrequest("/order/cancel/replace/", params, result); + }; + + int getOrderStatus(string &result, const long long &order_id) { - return badDepositMethod; - } + string params = "{\"request\":\"/v1/order/status\",\"nonce\":\"" + getTonce() + "\""; + params += ",\"order_id\":" + to_string(order_id); + params += "}"; + return DoPOSTrequest("/order/status/", params, result); + }; - string params = "{\"request\":\"/v1/history/movements\",\"nonce\":\"" + getTonce() + "\""; - params += ",\"currency\":\"" + currency + "\""; - if (method != "all") + int getActiveOrders(string &result) { - params += ",\"method\":\"" + method + "\""; - } - params += ",\"since\":\"" + to_string(since) + "\""; - params += ",\"until\":\"" + (!until ? getTonce() : to_string(until)) + "\""; - params += ",\"limit\":" + to_string(limit); - params += "}"; - return DoPOSTrequest("/history/movements/", params, result); - }; - - int getPastTrades(string &result, - const string &symbol, - const time_t ×tamp, - const time_t &until = 0, - const int &limit_trades = 500, - const bool reverse = 0) - { - // Is symbol valid ? - if(!inArray(symbol, _symbols)) + string params = "{\"request\":\"/v1/orders\",\"nonce\":\"" + getTonce() + "\""; + params += "}"; + return DoPOSTrequest("/orders/", params, result); + }; + + int getOrdersHistory(string &result, const int &limit = 50) { - return badSymbol; - } + string params = "{\"request\":\"/v1/orders/hist\",\"nonce\":\"" + getTonce() + "\""; + params += ",\"limit\":" + to_string(limit); + params += "}"; + return DoPOSTrequest("/orders/hist/", params, result); + }; - string params = "{\"request\":\"/v1/mytrades\",\"nonce\":\"" + getTonce() + "\""; - params += ",\"symbol\":\"" + symbol + "\""; - params += ",\"timestamp\":\"" + to_string(timestamp) + "\""; - params += ",\"until\":\"" + (!until ? getTonce() : to_string(until)) + "\""; - params += ",\"limit_trades\":" + to_string(limit_trades); - params += ",\"reverse\":" + to_string(reverse); - params += "}"; - return DoPOSTrequest("/mytrades/", params, result); - }; - - // Margin funding - int newOffer(string &result, - const string ¤cy, - const double &amount, - const float &rate, - const int &period, - const string &direction) - { - // Is currency valid ? - if(!inArray(currency, _currencies)) + + // Positions + int getActivePositions(string &result) { - return badCurrency; - } + string params = "{\"request\":\"/v1/positions\",\"nonce\":\"" + getTonce() + "\""; + params += "}"; + return DoPOSTrequest("/positions/", params, result); + }; - string params = "{\"request\":\"/v1/offer/new\",\"nonce\":\"" + getTonce() + "\""; - params += ",\"currency\":\"" + currency + "\""; - params += ",\"amount\":\"" + to_string(amount) + "\""; - params += ",\"rate\":\"" + to_string(rate) + "\""; - params += ",\"period\":" + to_string(period); - params += ",\"direction\":\"" + direction + "\""; - params += "}"; - return DoPOSTrequest("/offer/new/", params, result); - }; - - int cancelOffer(string &result, const long long &offer_id) - { - string params = "{\"request\":\"/v1/offer/cancel\",\"nonce\":\"" + getTonce() + "\""; - params += ",\"offer_id\":" + to_string(offer_id); - params += "}"; - return DoPOSTrequest("/offer/cancel/", params, result); - }; - - int getOfferStatus(string &result, const long long &offer_id) - { - string params = "{\"request\":\"/v1/offer/status\",\"nonce\":\"" + getTonce() + "\""; - params += ",\"offer_id\":" + to_string(offer_id); - params += "}"; - return DoPOSTrequest("/offer/status/", params, result); - }; - - int getActiveCredits(string &result) - { - string params = "{\"request\":\"/v1/credits\",\"nonce\":\"" + getTonce() + "\""; - params += "}"; - return DoPOSTrequest("/credits/", params, result); - }; - - int getOffers(string &result) - { - string params = "{\"request\":\"/v1/offers\",\"nonce\":\"" + getTonce() + "\""; - params += "}"; - return DoPOSTrequest("/offers/", params, result); - }; - - int getOffersHistory(string &result, const int &limit) - { - string params = "{\"request\":\"/v1/offers/hist\",\"nonce\":\"" + getTonce() + "\""; - params += ",\"limit\":" + to_string(limit); - params += "}"; - return DoPOSTrequest("/offers/hist/", params, result); - }; - - // There is ambiguity in the "symbol" parameter value for this call. - // It should be "currency" not "symbol". - // Typical values for "symbol" are trading pairs such as "btcusd", "btcltc" ... - // Typical values for "currency" are "btc", "ltc" ... - int getPastFundingTrades(string &result, - const string ¤cy, - const time_t &until = 0, - const int &limit_trades = 50) - { - // Is currency valid ? - if(!inArray(currency, _currencies)) + int claimPosition(string &result, + long long &position_id, + const double &amount) { - return badCurrency; - } + string params = "{\"request\":\"/v1/position/claim\",\"nonce\":\"" + getTonce() + "\""; + params += ",\"position_id\":" + to_string(position_id); + params += ",\"amount\":\"" + to_string(amount) + "\""; + params += "}"; + return DoPOSTrequest("/position/claim/", params, result); + }; - string params = "{\"request\":\"/v1/mytrades_funding\",\"nonce\":\"" + getTonce() + "\""; - // param inconsistency in BFX API, symbol should be currency - params += ",\"symbol\":\"" + currency + "\""; - params += ",\"until\":" + to_string(until); - params += ",\"limit_trades\":" + to_string(limit_trades); - params += "}"; - return DoPOSTrequest("/mytrades_funding/", params, result); - }; - - int getTakenFunds(string &result) - { - string params = "{\"request\":\"/v1/taken_funds\",\"nonce\":\"" + getTonce() + "\""; - params += "}"; - return DoPOSTrequest("/taken_funds/", params, result); - }; - - int getUnusedTakenFunds(string &result) - { - string params = "{\"request\":\"/v1/unused_taken_funds\",\"nonce\":\"" + getTonce() + "\""; - params += "}"; - return DoPOSTrequest("/unused_taken_funds/", params, result); - }; - - int getTotalTakenFunds(string &result) - { - string params = "{\"request\":\"/v1/total_taken_funds\",\"nonce\":\"" + getTonce() + "\""; - params += "}"; - return DoPOSTrequest("/total_taken_funds/", params, result); - }; - - int closeLoan(string &result, const long long &offer_id) - { - string params = "{\"request\":\"/v1/funding/close\",\"nonce\":\"" + getTonce() + "\""; - params += ",\"swap_id\":" + to_string(offer_id); - params += "}"; - return DoPOSTrequest("/funding/close/", params, result); - }; - - int closePosition(string &result, const long long &position_id) - { - string params = "{\"request\":\"/v1/position/close\",\"nonce\":\"" + getTonce() + "\""; - params += ",\"position_id\":" + to_string(position_id); - params += "}"; - return DoPOSTrequest("/position/close/", params, result); - }; - -protected: - - // BitfinexAPI object cannot be copied - BitfinexAPI(const BitfinexAPI&); - BitfinexAPI& operator = (const BitfinexAPI&); - - ////////////////////////////////////////////////////////////////////////////// - // Private attributes - ////////////////////////////////////////////////////////////////////////////// - - unordered_set _symbols; // possible symbol pairs - unordered_set _currencies; // possible currencies - unordered_set _methods; // possible deposit methods - unordered_set _walletNames; // possible walletTypes - unordered_set _types; // possible Types (see new order endpoint) - string _WDconfFilePath; - string _APIurl; - string _accessKey, _secretKey; - CURL *_curl; - CURLcode _res; - - ////////////////////////////////////////////////////////////////////////////// - // Utility private methods - ////////////////////////////////////////////////////////////////////////////// - - int parseWDconfParams(string ¶ms) - { - using std::getline; - using std::ifstream; - using std::map; - using std::regex; - using std::regex_search; - using std::smatch; - - string line; - ifstream inFile; - map mParams; - inFile.open(_WDconfFilePath); - regex rgx("^(.*)\\b\\s*=\\s*(\"{0,1}.*\"{0,1})$"); - smatch match; - - // Create map with parameters - while (getline(inFile, line)) + // Historical data + int getBalanceHistory(string &result, + const string ¤cy, + const time_t &since = 0, + const time_t &until = 0, + const int &limit = 500, + const string &walletType = "all") { - // Skip comments, blank lines ... - if (isalpha(line[0])) + // Is currency valid ? + if(!inArray(currency, _currencies)) { - // ... and keys with empty values - if (regex_search(line, match, rgx) && match[2] != "\"\"") - mParams.emplace(match[1], match[2]); + return badCurrency; } - } + // Is wallet type valid ? + // Modified condition which accepts "all" value for all wallets balances together. + if(!inArray(walletType, _walletNames) && walletType != "all") + { + return badWalletType; + } + + string params = "{\"request\":\"/v1/history\",\"nonce\":\"" + getTonce() + "\""; + params += ",\"currency\":\"" + currency + "\""; + params += ",\"since\":\"" + to_string(since) + "\""; + params += ",\"until\":\"" + (!until ? getTonce() : to_string(until)) + "\""; + params += ",\"limit\":" + to_string(limit); + if (walletType != "all") + { + params += ",\"wallet\":\"" + walletType + "\""; + } + + params += "}"; + return DoPOSTrequest("/history/", params, result); + }; - // Check parameters - if (!mParams.count("withdraw_type") || - !mParams.count("walletselected") || - !mParams.count("amount")) + int getWithdrawalHistory(string &result, + const string ¤cy, + const string &method = "all", + const time_t &since = 0, + const time_t &until = 0, + const int &limit = 500) { - return requiredParamsMissing; - } + // Is currency valid ? + if(!inArray(currency, _currencies)) + { + return badCurrency; + } + // Is deposit method valid ? + if(!inArray(method, _methods) && method != "wire" && method != "all") + { + return badDepositMethod; + } + + string params = "{\"request\":\"/v1/history/movements\",\"nonce\":\"" + getTonce() + "\""; + params += ",\"currency\":\"" + currency + "\""; + if (method != "all") + { + params += ",\"method\":\"" + method + "\""; + } + params += ",\"since\":\"" + to_string(since) + "\""; + params += ",\"until\":\"" + (!until ? getTonce() : to_string(until)) + "\""; + params += ",\"limit\":" + to_string(limit); + params += "}"; + return DoPOSTrequest("/history/movements/", params, result); + }; - if (mParams["withdraw_type"] == "wire") + int getPastTrades(string &result, + const string &symbol, + const time_t ×tamp, + const time_t &until = 0, + const int &limit_trades = 500, + const bool reverse = 0) { - if (!mParams.count("account_number") || - !mParams.count("bank_name") || - !mParams.count("bank_address") || - !mParams.count("bank_city") || - !mParams.count("bank_country")) + // Is symbol valid ? + if(!inArray(symbol, _symbols)) { - return wireParamsMissing; + return badSymbol; } - } - else if (inArray(mParams["withdraw_type"], _methods)) + + string params = "{\"request\":\"/v1/mytrades\",\"nonce\":\"" + getTonce() + "\""; + params += ",\"symbol\":\"" + symbol + "\""; + params += ",\"timestamp\":\"" + to_string(timestamp) + "\""; + params += ",\"until\":\"" + (!until ? getTonce() : to_string(until)) + "\""; + params += ",\"limit_trades\":" + to_string(limit_trades); + params += ",\"reverse\":" + to_string(reverse); + params += "}"; + return DoPOSTrequest("/mytrades/", params, result); + }; + + // Margin funding + int newOffer(string &result, + const string ¤cy, + const double &amount, + const float &rate, + const int &period, + const string &direction) { - if(!mParams.count("address")) + // Is currency valid ? + if(!inArray(currency, _currencies)) { - return addressParamsMissing; + return badCurrency; } - } + + string params = "{\"request\":\"/v1/offer/new\",\"nonce\":\"" + getTonce() + "\""; + params += ",\"currency\":\"" + currency + "\""; + params += ",\"amount\":\"" + to_string(amount) + "\""; + params += ",\"rate\":\"" + to_string(rate) + "\""; + params += ",\"period\":" + to_string(period); + params += ",\"direction\":\"" + direction + "\""; + params += "}"; + return DoPOSTrequest("/offer/new/", params, result); + }; - // Create JSON string + int cancelOffer(string &result, const long long &offer_id) + { + string params = "{\"request\":\"/v1/offer/cancel\",\"nonce\":\"" + getTonce() + "\""; + params += ",\"offer_id\":" + to_string(offer_id); + params += "}"; + return DoPOSTrequest("/offer/cancel/", params, result); + }; - for (const auto ¶m : mParams) + int getOfferStatus(string &result, const long long &offer_id) { - params += ",\""; - params += param.first; - params += "\":"; - params += param.second; - } + string params = "{\"request\":\"/v1/offer/status\",\"nonce\":\"" + getTonce() + "\""; + params += ",\"offer_id\":" + to_string(offer_id); + params += "}"; + return DoPOSTrequest("/offer/status/", params, result); + }; - return 0; - }; - - int DoGETrequest(const string &UrlEndPoint, const string ¶ms, string &result) - { - if(_curl) + int getActiveCredits(string &result) { - string url = _APIurl + UrlEndPoint + params; + string params = "{\"request\":\"/v1/credits\",\"nonce\":\"" + getTonce() + "\""; + params += "}"; + return DoPOSTrequest("/credits/", params, result); + }; + + int getOffers(string &result) + { + string params = "{\"request\":\"/v1/offers\",\"nonce\":\"" + getTonce() + "\""; + params += "}"; + return DoPOSTrequest("/offers/", params, result); + }; + + int getOffersHistory(string &result, const int &limit) + { + string params = "{\"request\":\"/v1/offers/hist\",\"nonce\":\"" + getTonce() + "\""; + params += ",\"limit\":" + to_string(limit); + params += "}"; + return DoPOSTrequest("/offers/hist/", params, result); + }; + + // There is ambiguity in the "symbol" parameter value for this call. + // It should be "currency" not "symbol". + // Typical values for "symbol" are trading pairs such as "btcusd", "btcltc" ... + // Typical values for "currency" are "btc", "ltc" ... + int getPastFundingTrades(string &result, + const string ¤cy, + const time_t &until = 0, + const int &limit_trades = 50) + { + // Is currency valid ? + if(!inArray(currency, _currencies)) + { + return badCurrency; + } - _curl = curl_easy_init(); - curl_easy_setopt(_curl, CURLOPT_TIMEOUT, 30L); - curl_easy_setopt(_curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(_curl, CURLOPT_WRITEDATA, &result); - curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, WriteCallback); + string params = "{\"request\":\"/v1/mytrades_funding\",\"nonce\":\"" + getTonce() + "\""; + // param inconsistency in BFX API, symbol should be currency + params += ",\"symbol\":\"" + currency + "\""; + params += ",\"until\":" + to_string(until); + params += ",\"limit_trades\":" + to_string(limit_trades); + params += "}"; + return DoPOSTrequest("/mytrades_funding/", params, result); + }; + + int getTakenFunds(string &result) + { + string params = "{\"request\":\"/v1/taken_funds\",\"nonce\":\"" + getTonce() + "\""; + params += "}"; + return DoPOSTrequest("/taken_funds/", params, result); + }; + + int getUnusedTakenFunds(string &result) + { + string params = "{\"request\":\"/v1/unused_taken_funds\",\"nonce\":\"" + getTonce() + "\""; + params += "}"; + return DoPOSTrequest("/unused_taken_funds/", params, result); + }; + + int getTotalTakenFunds(string &result) + { + string params = "{\"request\":\"/v1/total_taken_funds\",\"nonce\":\"" + getTonce() + "\""; + params += "}"; + return DoPOSTrequest("/total_taken_funds/", params, result); + }; + + int closeLoan(string &result, const long long &offer_id) + { + string params = "{\"request\":\"/v1/funding/close\",\"nonce\":\"" + getTonce() + "\""; + params += ",\"swap_id\":" + to_string(offer_id); + params += "}"; + return DoPOSTrequest("/funding/close/", params, result); + }; + + int closePosition(string &result, const long long &position_id) + { + string params = "{\"request\":\"/v1/position/close\",\"nonce\":\"" + getTonce() + "\""; + params += ",\"position_id\":" + to_string(position_id); + params += "}"; + return DoPOSTrequest("/position/close/", params, result); + }; + + protected: + + // BitfinexAPI object cannot be copied + BitfinexAPI(const BitfinexAPI&); + BitfinexAPI& operator = (const BitfinexAPI&); + + ////////////////////////////////////////////////////////////////////////////// + // Private attributes + ////////////////////////////////////////////////////////////////////////////// + + unordered_set _symbols; // possible symbol pairs + unordered_set _currencies; // possible currencies + unordered_set _methods; // possible deposit methods + unordered_set _walletNames; // possible walletTypes + unordered_set _types; // possible Types (see new order endpoint) + string _WDconfFilePath; + string _APIurl; + string _accessKey, _secretKey; + CURL *_curl; + CURLcode _res; + + ////////////////////////////////////////////////////////////////////////////// + // Utility private methods + ////////////////////////////////////////////////////////////////////////////// + + int parseWDconfParams(string ¶ms) + { + using std::getline; + using std::ifstream; + using std::map; + using std::regex; + using std::regex_search; + using std::smatch; - _res = curl_easy_perform(_curl); + string line; + ifstream inFile; + map mParams; + inFile.open(_WDconfFilePath); + regex rgx("^(.*)\\b\\s*=\\s*(\"{0,1}.*\"{0,1})$"); + smatch match; - // libcurl internal error handling - if (_res != CURLE_OK) + // Create map with parameters + while (getline(inFile, line)) { - cout << "Libcurl error in DoGETrequest(), code:\n"; - return _res; + // Skip comments, blank lines ... + if (isalpha(line[0])) + { + // ... and keys with empty values + if (regex_search(line, match, rgx) && match[2] != "\"\"") + mParams.emplace(match[1], match[2]); + } } - return _res; - } - else - { - // curl not properly initialized curl = nullptr - return curlERR; - } - }; - - int DoPOSTrequest(const string &UrlEndPoint, const string ¶ms, string &result) - { - if(_curl) - { - string url = _APIurl + UrlEndPoint; - string payload; - string signature; - getBase64(params, payload); - getHmacSha384(_secretKey, payload, signature); - // Headers - struct curl_slist *httpHeaders = nullptr; - httpHeaders = curl_slist_append(httpHeaders, - ("X-BFX-APIKEY:" + _accessKey).c_str()); - httpHeaders = curl_slist_append(httpHeaders, - ("X-BFX-PAYLOAD:" + payload).c_str()); - httpHeaders = curl_slist_append(httpHeaders, - ("X-BFX-SIGNATURE:" + signature).c_str()); + // Check parameters + if (!mParams.count("withdraw_type") || + !mParams.count("walletselected") || + !mParams.count("amount")) + { + return requiredParamsMissing; + } - _curl = curl_easy_init(); - curl_easy_setopt(_curl, CURLOPT_HTTPHEADER, httpHeaders); - curl_easy_setopt(_curl, CURLOPT_POST, 1); - curl_easy_setopt(_curl, CURLOPT_POSTFIELDS, "\n"); - curl_easy_setopt(_curl, CURLOPT_TIMEOUT, 30L); - curl_easy_setopt(_curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(_curl, CURLOPT_VERBOSE, 0L); // debug option - curl_easy_setopt(_curl, CURLOPT_WRITEDATA, &result); - curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, WriteCallback); + if (mParams["withdraw_type"] == "wire") + { + if (!mParams.count("account_number") || + !mParams.count("bank_name") || + !mParams.count("bank_address") || + !mParams.count("bank_city") || + !mParams.count("bank_country")) + { + return wireParamsMissing; + } + } + else if (inArray(mParams["withdraw_type"], _methods)) + { + if(!mParams.count("address")) + { + return addressParamsMissing; + } + } - _res = curl_easy_perform(_curl); + // Create JSON string - // libcurl internal error handling - if (_res != CURLE_OK) + for (const auto ¶m : mParams) { - cout << "Libcurl error in DoPOSTrequest(), code:\n"; - return _res; + params += ",\""; + params += param.first; + params += "\":"; + params += param.second; } - return _res; - } - else + return 0; + }; + + int DoGETrequest(const string &UrlEndPoint, const string ¶ms, string &result) { - // curl not properly initialized curl = NULL - return curlERR; - } - }; - - ////////////////////////////////////////////////////////////////////////////// - // Utility private static methods - ////////////////////////////////////////////////////////////////////////////// - - static string bool2string(const bool &in) { return in ? "true" : "false"; }; - - static string getTonce() - { - using namespace std::chrono; + if(_curl) + { + string url = _APIurl + UrlEndPoint + params; + + _curl = curl_easy_init(); + curl_easy_setopt(_curl, CURLOPT_TIMEOUT, 30L); + curl_easy_setopt(_curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(_curl, CURLOPT_WRITEDATA, &result); + curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, WriteCallback); + + _res = curl_easy_perform(_curl); + + // libcurl internal error handling + if (_res != CURLE_OK) + { + cout << "Libcurl error in DoGETrequest(), code:\n"; + return _res; + } + return _res; + } + else + { + // curl not properly initialized curl = nullptr + return curlERR; + } + }; - milliseconds ms = duration_cast(system_clock::now().time_since_epoch()); + int DoPOSTrequest(const string &UrlEndPoint, const string ¶ms, string &result) + { + if(_curl) + { + string url = _APIurl + UrlEndPoint; + string payload; + string signature; + getBase64(params, payload); + getHmacSha384(_secretKey, payload, signature); + + // Headers + struct curl_slist *httpHeaders = nullptr; + httpHeaders = curl_slist_append(httpHeaders, + ("X-BFX-APIKEY:" + _accessKey).c_str()); + httpHeaders = curl_slist_append(httpHeaders, + ("X-BFX-PAYLOAD:" + payload).c_str()); + httpHeaders = curl_slist_append(httpHeaders, + ("X-BFX-SIGNATURE:" + signature).c_str()); + + _curl = curl_easy_init(); + curl_easy_setopt(_curl, CURLOPT_HTTPHEADER, httpHeaders); + curl_easy_setopt(_curl, CURLOPT_POST, 1); + curl_easy_setopt(_curl, CURLOPT_POSTFIELDS, "\n"); + curl_easy_setopt(_curl, CURLOPT_TIMEOUT, 30L); + curl_easy_setopt(_curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(_curl, CURLOPT_VERBOSE, 0L); // debug option + curl_easy_setopt(_curl, CURLOPT_WRITEDATA, &result); + curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, WriteCallback); + + _res = curl_easy_perform(_curl); + + // libcurl internal error handling + if (_res != CURLE_OK) + { + cout << "Libcurl error in DoPOSTrequest(), code:\n"; + return _res; + } + return _res; + + } + else + { + // curl not properly initialized curl = NULL + return curlERR; + } + }; - return to_string(ms.count()); - }; - - static int getBase64(const string &content, string &encoded) - { - using CryptoPP::Base64Encoder; - using CryptoPP::StringSink; - using CryptoPP::StringSource; + ////////////////////////////////////////////////////////////////////////////// + // Utility private static methods + ////////////////////////////////////////////////////////////////////////////// - byte buffer[1024] = {}; + static string bool2string(const bool &in) { return in ? "true" : "false"; }; - for (int i = 0; i < content.length(); ++i) + static string getTonce() { - buffer[i] = content[i]; + using namespace std::chrono; + + milliseconds ms = duration_cast(system_clock::now().time_since_epoch()); + + return to_string(ms.count()); }; - StringSource ss(buffer, - content.length(), - true, - new Base64Encoder(new StringSink(encoded), false)); + static int getBase64(const string &content, string &encoded) + { + using CryptoPP::Base64Encoder; + using CryptoPP::StringSink; + using CryptoPP::StringSource; + + byte buffer[1024] = {}; + + for (int i = 0; i < content.length(); ++i) + { + buffer[i] = content[i]; + }; + + StringSource ss(buffer, + content.length(), + true, + new Base64Encoder(new StringSink(encoded), false)); + + return 0; + }; - return 0; - }; - - static int getHmacSha384(const string &key, const string &content, string &digest) - { - using CryptoPP::HashFilter; - using CryptoPP::HexEncoder; - using CryptoPP::HMAC; - using CryptoPP::SecByteBlock; - using CryptoPP::StringSink; - using CryptoPP::StringSource; - using CryptoPP::SHA384; - using std::transform; - - SecByteBlock byteKey((const byte*)key.data(), key.size()); - string mac; - digest.clear(); - - HMAC hmac(byteKey, byteKey.size()); - StringSource ss1(content, true, new HashFilter(hmac, new StringSink(mac))); - StringSource ss2(mac, true, new HexEncoder(new StringSink(digest))); - transform(digest.cbegin(), digest.cend(), digest.begin(), ::tolower); - - return 0; - }; - - // Curl write callback function. Appends fetched *content to *userp pointer. - // *userp pointer is set up by curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result) line. - // In this case *userp will point to result. - static size_t WriteCallback(void *response, size_t size, size_t nmemb, void *userp) - { - (static_cast(userp))->append(static_cast(response)); - return size * nmemb; - }; - - static bool inArray(const string &value, const unordered_set &inputSet) - { - return (inputSet.find(value) != inputSet.cend()) ? true : false; + static int getHmacSha384(const string &key, const string &content, string &digest) + { + using CryptoPP::HashFilter; + using CryptoPP::HexEncoder; + using CryptoPP::HMAC; + using CryptoPP::SecByteBlock; + using CryptoPP::StringSink; + using CryptoPP::StringSource; + using CryptoPP::SHA384; + using std::transform; + + SecByteBlock byteKey((const byte*)key.data(), key.size()); + string mac; + digest.clear(); + + HMAC hmac(byteKey, byteKey.size()); + StringSource ss1(content, true, new HashFilter(hmac, new StringSink(mac))); + StringSource ss2(mac, true, new HexEncoder(new StringSink(digest))); + transform(digest.cbegin(), digest.cend(), digest.begin(), ::tolower); + + return 0; + }; + + // Curl write callback function. Appends fetched *content to *userp pointer. + // *userp pointer is set up by curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result) line. + // In this case *userp will point to result. + static size_t WriteCallback(void *response, size_t size, size_t nmemb, void *userp) + { + (static_cast(userp))->append(static_cast(response)); + return size * nmemb; + }; + + static bool inArray(const string &value, const unordered_set &inputSet) + { + return (inputSet.find(value) != inputSet.cend()) ? true : false; + }; }; -}; +} From 40f27a97f2daa6a7c1738b440d0034ece9d19f9d Mon Sep 17 00:00:00 2001 From: Maple Date: Fri, 22 Jun 2018 10:10:35 +0200 Subject: [PATCH 03/43] typedef to using copyctor and copyassor public and delete --- example.cpp | 2 +- include/BitfinexAPI.hpp | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/example.cpp b/example.cpp index 737339c..4f21950 100644 --- a/example.cpp +++ b/example.cpp @@ -37,7 +37,7 @@ int main(int argc, char *argv[]) string response; int errCode = 0; - // errCode = bfxAPI.getTicker(response, "btcusd"); + errCode = bfxAPI.getTicker(response, "btcusd"); // errCode = bfxAPI.getStats(response, "btcusd"); // errCode = bfxAPI.getFundingBook(response, "USD", 50, 50); // errCode = bfxAPI.getOrderBook(response, "btcusd", 50, 50, 1); diff --git a/include/BitfinexAPI.hpp b/include/BitfinexAPI.hpp index 2bce5c1..2cdc41a 100644 --- a/include/BitfinexAPI.hpp +++ b/include/BitfinexAPI.hpp @@ -95,9 +95,8 @@ namespace BfxAPI string side; string type; }; - typedef vector vOrders; - - typedef vector vIds; + using vOrders = vector; + using vIds = vector; ////////////////////////////////////////////////////////////////////////////// // Constructor - destructor @@ -169,6 +168,10 @@ namespace BfxAPI }; } + // BitfinexAPI object cannot be copied + BitfinexAPI(const BitfinexAPI&) = delete; + BitfinexAPI& operator = (const BitfinexAPI&) = delete; + ~BitfinexAPI() { curl_easy_cleanup(_curl); @@ -795,10 +798,6 @@ namespace BfxAPI protected: - // BitfinexAPI object cannot be copied - BitfinexAPI(const BitfinexAPI&); - BitfinexAPI& operator = (const BitfinexAPI&); - ////////////////////////////////////////////////////////////////////////////// // Private attributes ////////////////////////////////////////////////////////////////////////////// From 0b2b2df634a803ca6d199f034c9fddeecbd64b1d Mon Sep 17 00:00:00 2001 From: Maple Date: Fri, 22 Jun 2018 10:41:01 +0200 Subject: [PATCH 04/43] const accessor --- include/BitfinexAPI.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/BitfinexAPI.hpp b/include/BitfinexAPI.hpp index 2cdc41a..5f4f784 100644 --- a/include/BitfinexAPI.hpp +++ b/include/BitfinexAPI.hpp @@ -181,7 +181,7 @@ namespace BfxAPI // Accessors ////////////////////////////////////////////////////////////////////////////// - string getWDconfFilePath() const { return _WDconfFilePath; } + const string& getWDconfFilePath() const { return _WDconfFilePath; } void setWDconfFilePath(const string &path) { _WDconfFilePath = path; } From e2cf3d7b71bc97b6dbb2acdbc1da673d6797ff66 Mon Sep 17 00:00:00 2001 From: Maple Date: Fri, 22 Jun 2018 19:43:52 +0200 Subject: [PATCH 05/43] comments edit --- include/BitfinexAPI.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/BitfinexAPI.hpp b/include/BitfinexAPI.hpp index 5f4f784..8a3ee04 100644 --- a/include/BitfinexAPI.hpp +++ b/include/BitfinexAPI.hpp @@ -111,6 +111,7 @@ namespace BfxAPI _APIurl("https://api.bitfinex.com/v1"), _curl(curl_easy_init()) { + // populate _symbols directly from Bitfinex getSymbols endpoint string temp; getSymbols(temp); jsonutils::arrayToUset(_symbols, temp); @@ -799,7 +800,7 @@ namespace BfxAPI protected: ////////////////////////////////////////////////////////////////////////////// - // Private attributes + // Protected attributes ////////////////////////////////////////////////////////////////////////////// unordered_set _symbols; // possible symbol pairs @@ -814,7 +815,7 @@ namespace BfxAPI CURLcode _res; ////////////////////////////////////////////////////////////////////////////// - // Utility private methods + // Utility protected methods ////////////////////////////////////////////////////////////////////////////// int parseWDconfParams(string ¶ms) From 767d8f33928c30ead781258ed6cadc023a71577a Mon Sep 17 00:00:00 2001 From: Maple Date: Tue, 26 Jun 2018 11:52:00 +0200 Subject: [PATCH 06/43] jsonutils validates and parses with SAX and rjson --- doc/definitions.json | 11 ++ doc/withdraw.conf | 2 - example.cpp | 9 +- include/BitfinexAPI.hpp | 374 +++++++++++++++++++++++----------------- include/jsonutils.hpp | 167 ++++++++++++++++-- 5 files changed, 386 insertions(+), 177 deletions(-) create mode 100644 doc/definitions.json diff --git a/doc/definitions.json b/doc/definitions.json new file mode 100644 index 0000000..d7ad40f --- /dev/null +++ b/doc/definitions.json @@ -0,0 +1,11 @@ +{ + "flatJsonSchema": { + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Flat JSON string array scheme used in jsonutils::jsonStrToUset", + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + } +} diff --git a/doc/withdraw.conf b/doc/withdraw.conf index 7c16401..999ae28 100644 --- a/doc/withdraw.conf +++ b/doc/withdraw.conf @@ -1,5 +1,4 @@ /////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// // // CONFIGURATION FILE FOR Withdraw() method // @@ -7,7 +6,6 @@ // if you don't wish to receive confirmation emails. // /////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// // string, REQUIRED for wire and crypto // Can be one of the following ["bitcoin", "litecoin", "ethereum", "ethereumc", "mastercoin", "zcash", "monero", "wire"]. diff --git a/example.cpp b/example.cpp index 4f21950..98cd900 100644 --- a/example.cpp +++ b/example.cpp @@ -7,15 +7,18 @@ // ////////////////////////////////////////////////////////////////////////////// - +// std #include #include + +// BitfinexAPI #include "BitfinexAPI.hpp" +// namespaces using std::cout; -using std::ifstream; using std::endl; +using std::ifstream; using std::string; using BfxAPI::BitfinexAPI; @@ -43,7 +46,7 @@ int main(int argc, char *argv[]) // errCode = bfxAPI.getOrderBook(response, "btcusd", 50, 50, 1); // errCode = bfxAPI.getTrades(response, "btcusd", 0L, 50); // errCode = bfxAPI.getLends(response, "USD", 0L, 50); - // errCode = bfxAPI.getSymbols(response); + // errCode = bfxAPI.getSymbols(response); // errCode = bfxAPI.getSymbolDetails(response); cout << "Response: " << response << endl; diff --git a/include/BitfinexAPI.hpp b/include/BitfinexAPI.hpp index 8a3ee04..e8bdf91 100644 --- a/include/BitfinexAPI.hpp +++ b/include/BitfinexAPI.hpp @@ -1,3 +1,4 @@ +//////////////////////////////////////////////////////////////////////////////// // BitfinexAPI.hpp // // @@ -19,7 +20,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . // -////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// #pragma once @@ -47,6 +48,7 @@ #include "jsonutils.hpp" +// namespaces using std::cout; using std::string; using std::to_string; @@ -66,9 +68,9 @@ namespace BfxAPI { public: - ////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// // Enumerations - ////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// enum bfxERR // positive values for curl internal error codes { curlERR = -50, @@ -79,12 +81,13 @@ namespace BfxAPI requiredParamsMissing = -36, wireParamsMissing = -35, addressParamsMissing = -34, - badOrderType = -33 + badOrderType = -33, + jsonStrToUSetError = -10 }; - ////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// // Typedefs - ////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// // Structure for multiple new orders endpoint struct sOrder @@ -98,25 +101,25 @@ namespace BfxAPI using vOrders = vector; using vIds = vector; - ////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// // Constructor - destructor - ////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// explicit BitfinexAPI():BitfinexAPI("", "") {} explicit BitfinexAPI(const string &accessKey, const string &secretKey): - _accessKey(accessKey), - _secretKey(secretKey), - _WDconfFilePath("doc/withdraw.conf"), - _APIurl("https://api.bitfinex.com/v1"), - _curl(curl_easy_init()) + accessKey_(accessKey), + secretKey_(secretKey), + WDconfFilePath_("doc/withdraw.conf"), + APIurl_("https://api.bitfinex.com/v1"), + curl_(curl_easy_init()) { // populate _symbols directly from Bitfinex getSymbols endpoint string temp; getSymbols(temp); - jsonutils::arrayToUset(_symbols, temp); + jsonutils::jsonStrToUset(symbols_, temp); - _currencies = + currencies_ = { "BTG", "DSH", @@ -135,8 +138,9 @@ namespace BfxAPI "XRP", "ZEC" }; - // As found on https://bitfinex.readme.io/v1/reference#rest-auth-deposit - _methods = + // As found on + // https://bitfinex.readme.io/v1/reference#rest-auth-deposit + methods_ = { "bcash" "bitcoin", @@ -149,12 +153,12 @@ namespace BfxAPI "tetheruso", "zcash", }; - _walletNames = + walletNames_ = { "trading", "exchange", "deposit" }; // New order endpoint "type" parameter - _types = + types_ = { "market", "limit", @@ -175,31 +179,31 @@ namespace BfxAPI ~BitfinexAPI() { - curl_easy_cleanup(_curl); + curl_easy_cleanup(curl_); } - ////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// // Accessors - ////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// - const string& getWDconfFilePath() const { return _WDconfFilePath; } + const string& getWDconfFilePath() const { return WDconfFilePath_; } - void setWDconfFilePath(const string &path) { _WDconfFilePath = path; } + void setWDconfFilePath(const string &path) { WDconfFilePath_ = path; } void setKeys(const string &accessKey, const string &secretKey) { - this->_accessKey = accessKey; - this->_secretKey = secretKey; + this->accessKey_ = accessKey; + this->secretKey_ = secretKey; } - ////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// // Public endpoints - ////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// int getTicker(string &result, const string &symbol) { // Is symbol valid ? - if(!inArray(symbol, _symbols)) + if(!inArray(symbol, symbols_)) { return badSymbol; } @@ -210,7 +214,7 @@ namespace BfxAPI int getStats(string &result, const string &symbol) { // Is symbol valid ? - if(!inArray(symbol, _symbols)) + if(!inArray(symbol, symbols_)) { return badSymbol; } @@ -224,7 +228,7 @@ namespace BfxAPI const int &limit_asks = 50) { // Is currency valid ? - if(!inArray(currency, _currencies)) + if(!inArray(currency, currencies_)) { return badCurrency; } @@ -242,7 +246,7 @@ namespace BfxAPI const bool &group = 1) { // Is symbol valid ? - if(!inArray(symbol, _symbols)) + if(!inArray(symbol, symbols_)) { return badSymbol; } @@ -260,7 +264,7 @@ namespace BfxAPI const int &limit_trades = 50) { // Is symbol valid ? - if(!inArray(symbol, _symbols)) + if(!inArray(symbol, symbols_)) { return badSymbol; } @@ -277,7 +281,7 @@ namespace BfxAPI const int &limit_lends = 50) { // Is currency valid ? - if(!inArray(currency, _currencies)) + if(!inArray(currency, currencies_)) { return badCurrency; } @@ -298,28 +302,31 @@ namespace BfxAPI return DoGETrequest("/symbols_details/", "", result); }; - ////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// // Authenticated endpoints - ////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// // Account int getAccountInfo(string &result) { - string params = "{\"request\":\"/v1/account_infos\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/account_infos\",\"nonce\":\"" + + getTonce() + "\""; params += "}"; return DoPOSTrequest("/account_infos/", params, result); }; int getAccountFees(string &result) { - string params = "{\"request\":\"/v1/account_fees\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/account_fees\",\"nonce\":\"" + + getTonce() + "\""; params += "}"; return DoPOSTrequest("/account_fees/", params, result); }; int getSummary(string &result) { - string params = "{\"request\":\"/v1/summary\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/summary\",\"nonce\":\"" + + getTonce() + "\""; params += "}"; return DoPOSTrequest("/summary/", params, result); }; @@ -330,17 +337,18 @@ namespace BfxAPI const bool &renew = 0) { // Is deposit method valid ? - if(!inArray(method, _methods)) + if(!inArray(method, methods_)) { return badDepositMethod; } // Is walletType valid ? - if(!inArray(walletName, _walletNames)) + if(!inArray(walletName, walletNames_)) { return badWalletType; } - string params = "{\"request\":\"/v1/deposit/new\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/deposit/new\",\"nonce\":\"" + + getTonce() + "\""; params += ",\"method\":\"" + method + "\""; params += ",\"wallet_name\":\"" + walletName + "\""; params += ",\"renew\":" + to_string(renew); @@ -350,21 +358,24 @@ namespace BfxAPI int getKeyPermissions(string &result) { - string params = "{\"request\":\"/v1/key_info\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/key_info\",\"nonce\":\"" + + getTonce() + "\""; params += "}"; return DoPOSTrequest("/key_info/", params, result); }; int getMarginInfos(string &result) { - string params = "{\"request\":\"/v1/margin_infos\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/margin_infos\",\"nonce\":\"" + + getTonce() + "\""; params += "}"; return DoPOSTrequest("/margin_infos/", params, result); }; int getBalances(string &result) { - string params = "{\"request\":\"/v1/balances\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/balances\",\"nonce\":\"" + + getTonce() + "\""; params += "}"; return DoPOSTrequest("/balances/", params, result); }; @@ -376,17 +387,19 @@ namespace BfxAPI const string &walletto) { // Is currency valid ? - if(!inArray(currency, _currencies)) + if(!inArray(currency, currencies_)) { return badCurrency; } // Is walletType valid ? - if(!inArray(walletfrom, _walletNames) || !inArray(walletto, _walletNames)) + if(!inArray(walletfrom, walletNames_) || + !inArray(walletto, walletNames_)) { return badWalletType; } - string params = "{\"request\":\"/v1/transfer\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/transfer\",\"nonce\":\"" + + getTonce() + "\""; params += ",\"amount\":\"" + to_string(amount) + "\""; params += ",\"currency\":\"" + currency + "\""; params += ",\"walletfrom\":\"" + walletfrom + "\""; @@ -397,7 +410,8 @@ namespace BfxAPI int withdraw(string &result) // configure withdraw.conf file before use { - string params = "{\"request\":\"/v1/withdraw\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/withdraw\",\"nonce\":\"" + + getTonce() + "\""; // Add params from withdraw.conf int err = parseWDconfParams(params); @@ -421,17 +435,18 @@ namespace BfxAPI const double &buy_price_oco = 0) { // Is symbol valid ? - if(!inArray(symbol, _symbols)) + if(!inArray(symbol, symbols_)) { return badSymbol; } // Is order type valid ? - if(!inArray(type, _types)) + if(!inArray(type, types_)) { return badOrderType; } - string params = "{\"request\":\"/v1/order/new\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/order/new\",\"nonce\":\"" + + getTonce() + "\""; params += ",\"symbol\":\"" + symbol + "\""; params += ",\"amount\":\"" + to_string(amount) + "\""; params += ",\"price\":\"" + to_string(price) + "\""; @@ -448,7 +463,8 @@ namespace BfxAPI int newOrders(string &result, const vOrders &orders) { - string params = "{\"request\":\"/v1/order/new/multi\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/order/new/multi\",\"nonce\":\"" + + getTonce() + "\""; // Get pointer to last element in orders. We will not place // ',' character at the end of the last loop. @@ -474,7 +490,8 @@ namespace BfxAPI int cancelOrder(string &result, const long long &order_id) { - string params = "{\"request\":\"/v1/order/cancel\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/order/cancel\",\"nonce\":\"" + + getTonce() + "\""; params += ",\"order_id\":" + to_string(order_id); params += "}"; return DoPOSTrequest("/order/cancel/", params, result); @@ -482,7 +499,8 @@ namespace BfxAPI int cancelOrders(string &result, const vIds &vOrder_ids) { - string params = "{\"request\":\"/v1/order/cancel/multi\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/order/cancel/multi\",\"nonce\":\"" + + getTonce() + "\""; // Get pointer to last element in vOrders. We will not place // ',' character at the end of the last loop. @@ -504,7 +522,8 @@ namespace BfxAPI int cancelAllOrders(string &result) { - string params = "{\"request\":\"/v1/order/cancel/all\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/order/cancel/all\",\"nonce\":\"" + + getTonce() + "\""; params += "}"; return DoPOSTrequest("/order/cancel/all/", params, result); }; @@ -520,17 +539,18 @@ namespace BfxAPI const bool &use_remaining = 0) { // Is symbol valid ? - if(!inArray(symbol, _symbols)) + if(!inArray(symbol, symbols_)) { return badSymbol; } // Is order type valid ? - if(!inArray(type, _types)) + if(!inArray(type, types_)) { return badOrderType; } - string params = "{\"request\":\"/v1/order/cancel/replace\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/order/cancel/replace\",\"nonce\":\"" + + getTonce() + "\""; params += ",\"order_id\":" + to_string(order_id); params += ",\"symbol\":\"" + symbol + "\""; params += ",\"amount\":\"" + to_string(amount) + "\""; @@ -545,7 +565,8 @@ namespace BfxAPI int getOrderStatus(string &result, const long long &order_id) { - string params = "{\"request\":\"/v1/order/status\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/order/status\",\"nonce\":\"" + + getTonce() + "\""; params += ",\"order_id\":" + to_string(order_id); params += "}"; return DoPOSTrequest("/order/status/", params, result); @@ -553,14 +574,16 @@ namespace BfxAPI int getActiveOrders(string &result) { - string params = "{\"request\":\"/v1/orders\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/orders\",\"nonce\":\"" + + getTonce() + "\""; params += "}"; return DoPOSTrequest("/orders/", params, result); }; int getOrdersHistory(string &result, const int &limit = 50) { - string params = "{\"request\":\"/v1/orders/hist\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/orders/hist\",\"nonce\":\"" + + getTonce() + "\""; params += ",\"limit\":" + to_string(limit); params += "}"; return DoPOSTrequest("/orders/hist/", params, result); @@ -570,7 +593,8 @@ namespace BfxAPI // Positions int getActivePositions(string &result) { - string params = "{\"request\":\"/v1/positions\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/positions\",\"nonce\":\"" + + getTonce() + "\""; params += "}"; return DoPOSTrequest("/positions/", params, result); }; @@ -579,7 +603,8 @@ namespace BfxAPI long long &position_id, const double &amount) { - string params = "{\"request\":\"/v1/position/claim\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/position/claim\",\"nonce\":\"" + + getTonce() + "\""; params += ",\"position_id\":" + to_string(position_id); params += ",\"amount\":\"" + to_string(amount) + "\""; params += "}"; @@ -595,21 +620,24 @@ namespace BfxAPI const string &walletType = "all") { // Is currency valid ? - if(!inArray(currency, _currencies)) + if(!inArray(currency, currencies_)) { return badCurrency; } // Is wallet type valid ? - // Modified condition which accepts "all" value for all wallets balances together. - if(!inArray(walletType, _walletNames) && walletType != "all") + // Modified condition which accepts "all" value for all wallets + // balances together. + if(!inArray(walletType, walletNames_) && walletType != "all") { return badWalletType; } - string params = "{\"request\":\"/v1/history\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/history\",\"nonce\":\"" + + getTonce() + "\""; params += ",\"currency\":\"" + currency + "\""; params += ",\"since\":\"" + to_string(since) + "\""; - params += ",\"until\":\"" + (!until ? getTonce() : to_string(until)) + "\""; + params += ",\"until\":\"" + (!until ? getTonce() : to_string(until)) + + "\""; params += ",\"limit\":" + to_string(limit); if (walletType != "all") { @@ -628,24 +656,26 @@ namespace BfxAPI const int &limit = 500) { // Is currency valid ? - if(!inArray(currency, _currencies)) + if(!inArray(currency, currencies_)) { return badCurrency; } // Is deposit method valid ? - if(!inArray(method, _methods) && method != "wire" && method != "all") + if(!inArray(method, methods_) && method != "wire" && method != "all") { return badDepositMethod; } - string params = "{\"request\":\"/v1/history/movements\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/history/movements\",\"nonce\":\"" + + getTonce() + "\""; params += ",\"currency\":\"" + currency + "\""; if (method != "all") { params += ",\"method\":\"" + method + "\""; } params += ",\"since\":\"" + to_string(since) + "\""; - params += ",\"until\":\"" + (!until ? getTonce() : to_string(until)) + "\""; + params += ",\"until\":\"" + + (!until ? getTonce() : to_string(until)) + "\""; params += ",\"limit\":" + to_string(limit); params += "}"; return DoPOSTrequest("/history/movements/", params, result); @@ -659,15 +689,17 @@ namespace BfxAPI const bool reverse = 0) { // Is symbol valid ? - if(!inArray(symbol, _symbols)) + if(!inArray(symbol, symbols_)) { return badSymbol; } - string params = "{\"request\":\"/v1/mytrades\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/mytrades\",\"nonce\":\"" + + getTonce() + "\""; params += ",\"symbol\":\"" + symbol + "\""; params += ",\"timestamp\":\"" + to_string(timestamp) + "\""; - params += ",\"until\":\"" + (!until ? getTonce() : to_string(until)) + "\""; + params += ",\"until\":\"" + + (!until ? getTonce() : to_string(until)) + "\""; params += ",\"limit_trades\":" + to_string(limit_trades); params += ",\"reverse\":" + to_string(reverse); params += "}"; @@ -683,12 +715,13 @@ namespace BfxAPI const string &direction) { // Is currency valid ? - if(!inArray(currency, _currencies)) + if(!inArray(currency, currencies_)) { return badCurrency; } - string params = "{\"request\":\"/v1/offer/new\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/offer/new\",\"nonce\":\"" + + getTonce() + "\""; params += ",\"currency\":\"" + currency + "\""; params += ",\"amount\":\"" + to_string(amount) + "\""; params += ",\"rate\":\"" + to_string(rate) + "\""; @@ -700,7 +733,8 @@ namespace BfxAPI int cancelOffer(string &result, const long long &offer_id) { - string params = "{\"request\":\"/v1/offer/cancel\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/offer/cancel\",\"nonce\":\"" + + getTonce() + "\""; params += ",\"offer_id\":" + to_string(offer_id); params += "}"; return DoPOSTrequest("/offer/cancel/", params, result); @@ -708,7 +742,8 @@ namespace BfxAPI int getOfferStatus(string &result, const long long &offer_id) { - string params = "{\"request\":\"/v1/offer/status\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/offer/status\",\"nonce\":\"" + + getTonce() + "\""; params += ",\"offer_id\":" + to_string(offer_id); params += "}"; return DoPOSTrequest("/offer/status/", params, result); @@ -716,21 +751,24 @@ namespace BfxAPI int getActiveCredits(string &result) { - string params = "{\"request\":\"/v1/credits\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/credits\",\"nonce\":\"" + + getTonce() + "\""; params += "}"; return DoPOSTrequest("/credits/", params, result); }; int getOffers(string &result) { - string params = "{\"request\":\"/v1/offers\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/offers\",\"nonce\":\"" + + getTonce() + "\""; params += "}"; return DoPOSTrequest("/offers/", params, result); }; int getOffersHistory(string &result, const int &limit) { - string params = "{\"request\":\"/v1/offers/hist\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/offers/hist\",\"nonce\":\"" + + getTonce() + "\""; params += ",\"limit\":" + to_string(limit); params += "}"; return DoPOSTrequest("/offers/hist/", params, result); @@ -738,7 +776,8 @@ namespace BfxAPI // There is ambiguity in the "symbol" parameter value for this call. // It should be "currency" not "symbol". - // Typical values for "symbol" are trading pairs such as "btcusd", "btcltc" ... + // Typical values for "symbol" are trading pairs such as "btcusd", + // "btcltc" ... // Typical values for "currency" are "btc", "ltc" ... int getPastFundingTrades(string &result, const string ¤cy, @@ -746,12 +785,13 @@ namespace BfxAPI const int &limit_trades = 50) { // Is currency valid ? - if(!inArray(currency, _currencies)) + if(!inArray(currency, currencies_)) { return badCurrency; } - string params = "{\"request\":\"/v1/mytrades_funding\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/mytrades_funding\",\"nonce\":\"" + + getTonce() + "\""; // param inconsistency in BFX API, symbol should be currency params += ",\"symbol\":\"" + currency + "\""; params += ",\"until\":" + to_string(until); @@ -762,28 +802,32 @@ namespace BfxAPI int getTakenFunds(string &result) { - string params = "{\"request\":\"/v1/taken_funds\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/taken_funds\",\"nonce\":\"" + + getTonce() + "\""; params += "}"; return DoPOSTrequest("/taken_funds/", params, result); }; int getUnusedTakenFunds(string &result) { - string params = "{\"request\":\"/v1/unused_taken_funds\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/unused_taken_funds\",\"nonce\":\"" + + getTonce() + "\""; params += "}"; return DoPOSTrequest("/unused_taken_funds/", params, result); }; int getTotalTakenFunds(string &result) { - string params = "{\"request\":\"/v1/total_taken_funds\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/total_taken_funds\",\"nonce\":\"" + + getTonce() + "\""; params += "}"; return DoPOSTrequest("/total_taken_funds/", params, result); }; int closeLoan(string &result, const long long &offer_id) { - string params = "{\"request\":\"/v1/funding/close\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/funding/close\",\"nonce\":\"" + + getTonce() + "\""; params += ",\"swap_id\":" + to_string(offer_id); params += "}"; return DoPOSTrequest("/funding/close/", params, result); @@ -791,7 +835,8 @@ namespace BfxAPI int closePosition(string &result, const long long &position_id) { - string params = "{\"request\":\"/v1/position/close\",\"nonce\":\"" + getTonce() + "\""; + string params = "{\"request\":\"/v1/position/close\",\"nonce\":\"" + + getTonce() + "\""; params += ",\"position_id\":" + to_string(position_id); params += "}"; return DoPOSTrequest("/position/close/", params, result); @@ -799,24 +844,24 @@ namespace BfxAPI protected: - ////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// // Protected attributes - ////////////////////////////////////////////////////////////////////////////// - - unordered_set _symbols; // possible symbol pairs - unordered_set _currencies; // possible currencies - unordered_set _methods; // possible deposit methods - unordered_set _walletNames; // possible walletTypes - unordered_set _types; // possible Types (see new order endpoint) - string _WDconfFilePath; - string _APIurl; - string _accessKey, _secretKey; - CURL *_curl; - CURLcode _res; - - ////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + + unordered_set symbols_; // valid symbol pairs + unordered_set currencies_; // valid currencies + unordered_set methods_; // valid deposit methods + unordered_set walletNames_; // valid walletTypes + unordered_set types_; // valid Types (see new order endpoint) + string WDconfFilePath_; + string APIurl_; + string accessKey_, secretKey_; + CURL *curl_; + CURLcode res_; + + //////////////////////////////////////////////////////////////////////// // Utility protected methods - ////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// int parseWDconfParams(string ¶ms) { @@ -830,7 +875,7 @@ namespace BfxAPI string line; ifstream inFile; map mParams; - inFile.open(_WDconfFilePath); + inFile.open(WDconfFilePath_); regex rgx("^(.*)\\b\\s*=\\s*(\"{0,1}.*\"{0,1})$"); smatch match; @@ -865,7 +910,7 @@ namespace BfxAPI return wireParamsMissing; } } - else if (inArray(mParams["withdraw_type"], _methods)) + else if (inArray(mParams["withdraw_type"], methods_)) { if(!mParams.count("address")) { @@ -886,27 +931,29 @@ namespace BfxAPI return 0; }; - int DoGETrequest(const string &UrlEndPoint, const string ¶ms, string &result) + int DoGETrequest(const string &UrlEndPoint, + const string ¶ms, + string &result) { - if(_curl) + if(curl_) { - string url = _APIurl + UrlEndPoint + params; + string url = APIurl_ + UrlEndPoint + params; - _curl = curl_easy_init(); - curl_easy_setopt(_curl, CURLOPT_TIMEOUT, 30L); - curl_easy_setopt(_curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(_curl, CURLOPT_WRITEDATA, &result); - curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, WriteCallback); + curl_ = curl_easy_init(); + curl_easy_setopt(curl_, CURLOPT_TIMEOUT, 30L); + curl_easy_setopt(curl_, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &result); + curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, WriteCallback); - _res = curl_easy_perform(_curl); + res_ = curl_easy_perform(curl_); // libcurl internal error handling - if (_res != CURLE_OK) + if (res_ != CURLE_OK) { cout << "Libcurl error in DoGETrequest(), code:\n"; - return _res; + return res_; } - return _res; + return res_; } else { @@ -915,44 +962,49 @@ namespace BfxAPI } }; - int DoPOSTrequest(const string &UrlEndPoint, const string ¶ms, string &result) + int DoPOSTrequest(const string &UrlEndPoint, + const string ¶ms, + string &result) { - if(_curl) + if(curl_) { - string url = _APIurl + UrlEndPoint; + string url = APIurl_ + UrlEndPoint; string payload; string signature; getBase64(params, payload); - getHmacSha384(_secretKey, payload, signature); + getHmacSha384(secretKey_, payload, signature); // Headers struct curl_slist *httpHeaders = nullptr; - httpHeaders = curl_slist_append(httpHeaders, - ("X-BFX-APIKEY:" + _accessKey).c_str()); - httpHeaders = curl_slist_append(httpHeaders, - ("X-BFX-PAYLOAD:" + payload).c_str()); - httpHeaders = curl_slist_append(httpHeaders, - ("X-BFX-SIGNATURE:" + signature).c_str()); + httpHeaders = + curl_slist_append(httpHeaders, + ("X-BFX-APIKEY:" + accessKey_).c_str()); + httpHeaders = + curl_slist_append(httpHeaders, + ("X-BFX-PAYLOAD:" + payload).c_str()); + httpHeaders = + curl_slist_append(httpHeaders, + ("X-BFX-SIGNATURE:" + signature).c_str()); - _curl = curl_easy_init(); - curl_easy_setopt(_curl, CURLOPT_HTTPHEADER, httpHeaders); - curl_easy_setopt(_curl, CURLOPT_POST, 1); - curl_easy_setopt(_curl, CURLOPT_POSTFIELDS, "\n"); - curl_easy_setopt(_curl, CURLOPT_TIMEOUT, 30L); - curl_easy_setopt(_curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(_curl, CURLOPT_VERBOSE, 0L); // debug option - curl_easy_setopt(_curl, CURLOPT_WRITEDATA, &result); - curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, WriteCallback); + curl_ = curl_easy_init(); + curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, httpHeaders); + curl_easy_setopt(curl_, CURLOPT_POST, 1); + curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, "\n"); + curl_easy_setopt(curl_, CURLOPT_TIMEOUT, 30L); + curl_easy_setopt(curl_, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl_, CURLOPT_VERBOSE, 0L); // debug option + curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &result); + curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, WriteCallback); - _res = curl_easy_perform(_curl); + res_ = curl_easy_perform(curl_); // libcurl internal error handling - if (_res != CURLE_OK) + if (res_ != CURLE_OK) { cout << "Libcurl error in DoPOSTrequest(), code:\n"; - return _res; + return res_; } - return _res; + return res_; } else @@ -962,17 +1014,21 @@ namespace BfxAPI } }; - ////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// // Utility private static methods - ////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// - static string bool2string(const bool &in) { return in ? "true" : "false"; }; + static string bool2string(const bool &in) + { + return in ? "true" : "false"; + }; static string getTonce() { using namespace std::chrono; - milliseconds ms = duration_cast(system_clock::now().time_since_epoch()); + milliseconds ms = + duration_cast(system_clock::now().time_since_epoch()); return to_string(ms.count()); }; @@ -998,7 +1054,9 @@ namespace BfxAPI return 0; }; - static int getHmacSha384(const string &key, const string &content, string &digest) + static int getHmacSha384(const string &key, + const string &content, + string &digest) { using CryptoPP::HashFilter; using CryptoPP::HexEncoder; @@ -1014,23 +1072,29 @@ namespace BfxAPI digest.clear(); HMAC hmac(byteKey, byteKey.size()); - StringSource ss1(content, true, new HashFilter(hmac, new StringSink(mac))); + StringSource ss1(content, true, + new HashFilter(hmac, new StringSink(mac))); StringSource ss2(mac, true, new HexEncoder(new StringSink(digest))); transform(digest.cbegin(), digest.cend(), digest.begin(), ::tolower); return 0; }; - // Curl write callback function. Appends fetched *content to *userp pointer. - // *userp pointer is set up by curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result) line. - // In this case *userp will point to result. - static size_t WriteCallback(void *response, size_t size, size_t nmemb, void *userp) + // Curl write callback function. Appends fetched *content to *userp + // pointer. *userp pointer is set up by + // curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result) line. + // In this case *userp points to result. + static size_t WriteCallback(void *response, + size_t size, + size_t nmemb, + void *userp) { (static_cast(userp))->append(static_cast(response)); return size * nmemb; }; - static bool inArray(const string &value, const unordered_set &inputSet) + static bool inArray(const string &value, + const unordered_set &inputSet) { return (inputSet.find(value) != inputSet.cend()) ? true : false; }; diff --git a/include/jsonutils.hpp b/include/jsonutils.hpp index 8428831..3308ac8 100644 --- a/include/jsonutils.hpp +++ b/include/jsonutils.hpp @@ -1,33 +1,166 @@ -// JSON Utility routines for BitfinexAPI +//////////////////////////////////////////////////////////////////////////////// +// +// JSON utility routines for BitfinexAPI +// +//////////////////////////////////////////////////////////////////////////////// #pragma once +// rapidjson +#include "rapidjson/document.h" +#include "rapidjson/error/en.h" +#include "rapidjson/filereadstream.h" +#include "rapidjson/schema.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/writer.h" +// std +#include +#include #include -#include -using std::unordered_set; +// namespaces +using std::cerr; +using std::cout; +using std::endl; using std::string; -using std::stringstream; +using std::unordered_set; +namespace rj = rapidjson; + +/////////////////////////////////////////////////////////////////////////////// +// Routines +/////////////////////////////////////////////////////////////////////////////// namespace jsonutils { - void arrayToUset(unordered_set &uSet, string &array) + // Helper class resolving remote schema for schema $ref operator + class MyRemoteSchemaDocumentProvider: public rj::IRemoteSchemaDocumentProvider { - stringstream ss(array); - - while(ss.good()) - { - string substr; - getline(ss, substr, ','); - substr.erase(std::remove_if(substr.begin(), - substr.end(), - [](auto const &c) -> bool { - return !std::isalnum(c);}), - substr.end()); - uSet.emplace(substr); + public: + + MyRemoteSchemaDocumentProvider() + { + FILE *pFileIn = fopen("doc/definitions.json", "r"); // non-Windows use "r" + char readBuffer[65536]; + rj::FileReadStream fReadStream(pFileIn, readBuffer, sizeof(readBuffer)); + rj::Document d; + d.ParseStream(fReadStream); + fclose(pFileIn); + remoteSchemaDoc = new rj::SchemaDocument(d); + }; + ~MyRemoteSchemaDocumentProvider() + { + delete remoteSchemaDoc; + }; + + private: + + rj::SchemaDocument *remoteSchemaDoc; + + virtual const rj::SchemaDocument* + GetRemoteDocument(const char* uri, rj::SizeType length) + { + // Resolve the URI and return a pointer to that schema + return remoteSchemaDoc; + } + }; + + // SAX events helper struct for jsonStrToUset() routine + struct jsonStrToUsetHandler: + public rj::BaseReaderHandler, jsonStrToUsetHandler> + { + // Constructor + jsonStrToUsetHandler():state_(State::kExpectArrayStart) {} + + // SAX events handlers + bool StartArray() + { + switch (state_) + { + case State::kExpectArrayStart: + state_ = State::kExpectValueOrArrayEnd; + return true; + default: + return false; + } + } + + bool String(const char *str, rj::SizeType length, bool) + { + switch (state_) + { + case State::kExpectValueOrArrayEnd: + handlerUSet_.emplace(str); + return true; + default: + return false; + } + } + + bool EndArray(rj::SizeType) + { + return state_ == State::kExpectValueOrArrayEnd; + } + + bool Default() { return false; } // All other events are invalid. + + // Handler attributes + std::unordered_set handlerUSet_; // output uSet + enum class State // valid states + { + kExpectArrayStart, + kExpectValueOrArrayEnd, + } state_; + }; + + int jsonStrToUset(unordered_set &uSet, string &jsonStr) + { + // Create schema $ref resolver + rj::Document sd; + string schema = "{ \"$ref\": \"doc/definitions.json#/flatJsonSchema\" }"; + sd.Parse(schema.c_str()); + MyRemoteSchemaDocumentProvider provider; + rj::SchemaDocument schemaDoc(sd, 0, 0, &provider); + + // Create SAX events handler which contains parsed uSet after successful + // parsing + jsonStrToUsetHandler handler; + + // Create schema validator + rj::GenericSchemaValidator validator(schemaDoc, handler); + + // Create reader + rj::Reader reader; + + // Create input JSON StringStream + rj::StringStream ss(jsonStr.c_str()); + + // Parse and validate + if (!reader.Parse(ss, validator)) + { + // Input JSON is invalid according to the schema + // Output diagnostic information + cerr << "Error(offset " << reader.GetErrorOffset() << "): "; + cerr << GetParseError_En(reader.GetParseErrorCode()) << endl; + + if (!validator.IsValid()) + { + rj::StringBuffer sb; + validator.GetInvalidSchemaPointer().StringifyUriFragment(sb); + cout << "Invalid schema: " << sb.GetString() << endl; + cout << "Invalid keyword: " << validator.GetInvalidSchemaKeyword() << endl; + sb.Clear(); + validator.GetInvalidDocumentPointer().StringifyUriFragment(sb); + cout << "Invalid document: " << sb.GetString() << endl; + } + return -10; + } + else + { + uSet.swap(handler.handlerUSet_); + return 0; } } } From 13ad9bfdf014bb6f52c7047e8aed4d03db839223 Mon Sep 17 00:00:00 2001 From: Maple Date: Wed, 27 Jun 2018 20:52:26 +0200 Subject: [PATCH 07/43] code condesated, work on fluent api --- example.cpp | 4 +- example2.cpp | 63 ++++++++++ include/BitfinexAPI.hpp | 246 +++++++++++++++++++++------------------- 3 files changed, 197 insertions(+), 116 deletions(-) create mode 100644 example2.cpp diff --git a/example.cpp b/example.cpp index 98cd900..2626485 100644 --- a/example.cpp +++ b/example.cpp @@ -40,13 +40,13 @@ int main(int argc, char *argv[]) string response; int errCode = 0; - errCode = bfxAPI.getTicker(response, "btcusd"); + // errCode = bfxAPI.getTicker(response, "btcusd"); // errCode = bfxAPI.getStats(response, "btcusd"); // errCode = bfxAPI.getFundingBook(response, "USD", 50, 50); // errCode = bfxAPI.getOrderBook(response, "btcusd", 50, 50, 1); // errCode = bfxAPI.getTrades(response, "btcusd", 0L, 50); // errCode = bfxAPI.getLends(response, "USD", 0L, 50); - // errCode = bfxAPI.getSymbols(response); + errCode = bfxAPI.getSymbols(response); // errCode = bfxAPI.getSymbolDetails(response); cout << "Response: " << response << endl; diff --git a/example2.cpp b/example2.cpp new file mode 100644 index 0000000..4e8d7cc --- /dev/null +++ b/example2.cpp @@ -0,0 +1,63 @@ +////////////////////////////////////////////////////////////////////////////// +// +// example.cpp +// +// +// Bitfinex REST API C++ client - examples +// +////////////////////////////////////////////////////////////////////////////// + +// std +#include +#include + +// BitfinexAPI +#include "BitfinexAPI.hpp" + + +// namespaces +using std::cout; +using std::endl; +using std::ifstream; +using std::string; + + +int main(int argc, char *argv[]) +{ + ///////////////////////////////////////////////////////////////////////// + // Examples + // Note that default values are not mandatory. See BitfinexAPI.hpp + // for details. + ///////////////////////////////////////////////////////////////////////// + + ///////////////////////////////////////////////////////////////////////// + // GET REQUESTS - unauthenticated endpoints + ///////////////////////////////////////////////////////////////////////// + + BfxAPI::BitfinexAPI bfxAPI; + + bfxAPI.getTicker("btcusd"); + if (!bfxAPI.hasApiError()) + cout << bfxAPI.strResponse() << endl; + else + { + // see bfxERR enum in BitfinexAPI.hpp::BitfinexAPI + cout << bfxAPI.getBfxApiStatusCode() << endl; + // see https://curl.haxx.se/libcurl/c/libcurl-errors.html + cout << bfxAPI.getCurlStatusCode() << endl; + } + + bfxAPI.getStats("btcusd"); + if (!bfxAPI.hasApiError()) + cout << bfxAPI.strResponse() << endl; + else + { + // see bfxERR enum in BitfinexAPI.hpp::BitfinexAPI + cout << bfxAPI.getBfxApiStatusCode() << endl; + // see https://curl.haxx.se/libcurl/c/libcurl-errors.html + cout << bfxAPI.getCurlStatusCode() << endl; + } + + return 0; + +} diff --git a/include/BitfinexAPI.hpp b/include/BitfinexAPI.hpp index e8bdf91..7dda72c 100644 --- a/include/BitfinexAPI.hpp +++ b/include/BitfinexAPI.hpp @@ -50,6 +50,7 @@ // namespaces using std::cout; +using std::endl; using std::string; using std::to_string; using std::unordered_set; @@ -71,18 +72,20 @@ namespace BfxAPI //////////////////////////////////////////////////////////////////////// // Enumerations //////////////////////////////////////////////////////////////////////// - enum bfxERR // positive values for curl internal error codes + + enum bfxERR { - curlERR = -50, - badSymbol = -40, - badCurrency = -39, - badDepositMethod = -38, - badWalletType = -37, - requiredParamsMissing = -36, - wireParamsMissing = -35, - addressParamsMissing = -34, - badOrderType = -33, - jsonStrToUSetError = -10 + noError = 0, + curlERR, + badSymbol, + badCurrency, + badDepositMethod, + badWalletType, + requiredParamsMissing, + wireParamsMissing, + addressParamsMissing, + badOrderType, + jsonStrToUSetError }; //////////////////////////////////////////////////////////////////////// @@ -112,12 +115,15 @@ namespace BfxAPI secretKey_(secretKey), WDconfFilePath_("doc/withdraw.conf"), APIurl_("https://api.bitfinex.com/v1"), - curl_(curl_easy_init()) + curlGET_(curl_easy_init()), + curlPOST_(curl_easy_init()), + curlStatusCode_(CURLE_OK), + bfxApiStatusCode_(noError) { // populate _symbols directly from Bitfinex getSymbols endpoint - string temp; - getSymbols(temp); - jsonutils::jsonStrToUset(symbols_, temp); + getSymbols(); + jsonutils::jsonStrToUset(symbols_, result_); + result_.clear(); currencies_ = { @@ -179,17 +185,29 @@ namespace BfxAPI ~BitfinexAPI() { - curl_easy_cleanup(curl_); + curl_easy_cleanup(curlGET_); + curl_easy_cleanup(curlPOST_); } //////////////////////////////////////////////////////////////////////// // Accessors //////////////////////////////////////////////////////////////////////// - const string& getWDconfFilePath() const { return WDconfFilePath_; } + // Getters + string getWDconfFilePath() const { return WDconfFilePath_; } + int getBfxApiStatusCode() const { return bfxApiStatusCode_; } + int getCurlStatusCode() const { return curlStatusCode_; } + const string& strResponse() const { return result_ ; } + bool hasApiError() const + { + if (bfxApiStatusCode_ == noError && curlStatusCode_ == CURLE_OK) + return false; + else + return true; + } + // Setters void setWDconfFilePath(const string &path) { WDconfFilePath_ = path; } - void setKeys(const string &accessKey, const string &secretKey) { this->accessKey_ = accessKey; @@ -200,106 +218,108 @@ namespace BfxAPI // Public endpoints //////////////////////////////////////////////////////////////////////// - int getTicker(string &result, const string &symbol) + BitfinexAPI& getTicker(const string &symbol) { - // Is symbol valid ? - if(!inArray(symbol, symbols_)) - { - return badSymbol; - } + if (!inArray(symbol, symbols_)) + bfxApiStatusCode_ = badSymbol; + else + DoGETrequest("/pubticker/" + symbol, ""); - return DoGETrequest("/pubticker/" + symbol, "", result); + return *this; }; - int getStats(string &result, const string &symbol) + BitfinexAPI& getStats(const string &symbol) { - // Is symbol valid ? - if(!inArray(symbol, symbols_)) - { - return badSymbol; - } + if (!inArray(symbol, symbols_)) + bfxApiStatusCode_ = badSymbol; + else + DoGETrequest("/stats/" + symbol, ""); - return DoGETrequest("/stats/" + symbol, "", result); + return *this; }; - int getFundingBook(string &result, - const string ¤cy, - const int &limit_bids = 50, - const int &limit_asks = 50) + BitfinexAPI& getFundingBook(const string ¤cy, + const int &limit_bids = 50, + const int &limit_asks = 50) { - // Is currency valid ? - if(!inArray(currency, currencies_)) + if (!inArray(currency, currencies_)) + bfxApiStatusCode_ = badCurrency; + else { - return badCurrency; + string params = + "?limit_bids=" + to_string(limit_bids) + + "&limit_asks=" + to_string(limit_asks); + DoGETrequest("/lendbook/" + currency, params); } - string params = - "?limit_bids=" + to_string(limit_bids) + - "&limit_asks=" + to_string(limit_asks); - return DoGETrequest("/lendbook/" + currency, params, result); + return *this; }; - int getOrderBook(string &result, - const string &symbol, - const int &limit_bids = 50, - const int &limit_asks = 50, - const bool &group = 1) + BitfinexAPI& getOrderBook(const string &symbol, + const int &limit_bids = 50, + const int &limit_asks = 50, + const bool &group = 1) { - // Is symbol valid ? - if(!inArray(symbol, symbols_)) + if (!inArray(symbol, symbols_)) + bfxApiStatusCode_ = badSymbol; + else { - return badSymbol; + string params = + "?limit_bids=" + to_string(limit_bids) + + "&limit_asks=" + to_string(limit_asks) + + "&group=" + to_string(group); + DoGETrequest("/book/" + symbol, params); } - string params = - "?limit_bids=" + to_string(limit_bids) + - "&limit_asks=" + to_string(limit_asks) + - "&group=" + to_string(group); - return DoGETrequest("/book/" + symbol, params, result); + return *this; }; - int getTrades(string &result, - const string &symbol, - const time_t &since = 0, - const int &limit_trades = 50) + BitfinexAPI& getTrades(const string &symbol, + const time_t &since = 0, + const int &limit_trades = 50) { - // Is symbol valid ? - if(!inArray(symbol, symbols_)) + if (!inArray(symbol, symbols_)) + bfxApiStatusCode_ = badSymbol; + else { - return badSymbol; + string params = + "?timestamp=" + to_string(since) + + "&limit_trades=" + to_string(limit_trades); + DoGETrequest("/trades/" + symbol, params); } - string params = - "?timestamp=" + to_string(since) + - "&limit_trades=" + to_string(limit_trades); - return DoGETrequest("/trades/" + symbol, params, result); + return *this; }; - int getLends(string &result, - const string ¤cy, - const time_t &since = 0, - const int &limit_lends = 50) + BitfinexAPI& getLends(const string ¤cy, + const time_t &since = 0, + const int &limit_lends = 50) { - // Is currency valid ? - if(!inArray(currency, currencies_)) + if (!inArray(currency, currencies_)) + bfxApiStatusCode_ = badCurrency; + else { - return badCurrency; + string params = + "?timestamp=" + to_string(since) + + "&limit_lends=" + to_string(limit_lends); + DoGETrequest("/lends/" + currency, params); } - string params = - "?timestamp=" + to_string(since) + - "&limit_lends=" + to_string(limit_lends); - return DoGETrequest("/lends/" + currency, params, result); + return *this; }; - int getSymbols(string &result) + BitfinexAPI& getSymbols() { - return DoGETrequest("/symbols/", "", result); + DoGETrequest("/symbols/", ""); + + return *this; }; - int getSymbolDetails(string &result) + BitfinexAPI& getSymbolDetails() { - return DoGETrequest("/symbols_details/", "", result); + DoGETrequest("/symbols_details/", ""); + + return *this; }; //////////////////////////////////////////////////////////////////////// @@ -856,8 +876,11 @@ namespace BfxAPI string WDconfFilePath_; string APIurl_; string accessKey_, secretKey_; - CURL *curl_; - CURLcode res_; + CURL *curlGET_; + CURL *curlPOST_; + CURLcode curlStatusCode_; + bfxERR bfxApiStatusCode_; + string result_; //////////////////////////////////////////////////////////////////////// // Utility protected methods @@ -931,34 +954,30 @@ namespace BfxAPI return 0; }; - int DoGETrequest(const string &UrlEndPoint, - const string ¶ms, - string &result) + void DoGETrequest(const string &UrlEndPoint, const string ¶ms) { - if(curl_) + if(curlGET_) { string url = APIurl_ + UrlEndPoint + params; - curl_ = curl_easy_init(); - curl_easy_setopt(curl_, CURLOPT_TIMEOUT, 30L); - curl_easy_setopt(curl_, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &result); - curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, WriteCallback); + result_.clear(); + curl_easy_setopt(curlGET_, CURLOPT_TIMEOUT, 30L); + curl_easy_setopt(curlGET_, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curlGET_, CURLOPT_WRITEDATA, &result_); + curl_easy_setopt(curlGET_, CURLOPT_WRITEFUNCTION, WriteCallback); - res_ = curl_easy_perform(curl_); + curlStatusCode_ = curl_easy_perform(curlGET_); // libcurl internal error handling - if (res_ != CURLE_OK) + if (curlStatusCode_ != CURLE_OK) { - cout << "Libcurl error in DoGETrequest(), code:\n"; - return res_; + cout << "Libcurl error in DoGETrequest():\n"; + cout << "CURLcode: " << curlStatusCode_ << "\n"; } - return res_; } else { - // curl not properly initialized curl = nullptr - return curlERR; + cout << "curl not properly initialized curl = nullptr"; } }; @@ -966,7 +985,7 @@ namespace BfxAPI const string ¶ms, string &result) { - if(curl_) + if(curlPOST_) { string url = APIurl_ + UrlEndPoint; string payload; @@ -986,25 +1005,24 @@ namespace BfxAPI curl_slist_append(httpHeaders, ("X-BFX-SIGNATURE:" + signature).c_str()); - curl_ = curl_easy_init(); - curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, httpHeaders); - curl_easy_setopt(curl_, CURLOPT_POST, 1); - curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, "\n"); - curl_easy_setopt(curl_, CURLOPT_TIMEOUT, 30L); - curl_easy_setopt(curl_, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl_, CURLOPT_VERBOSE, 0L); // debug option - curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &result); - curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, WriteCallback); + curl_easy_setopt(curlPOST_, CURLOPT_HTTPHEADER, httpHeaders); + curl_easy_setopt(curlPOST_, CURLOPT_POST, 1); + curl_easy_setopt(curlPOST_, CURLOPT_POSTFIELDS, "\n"); + curl_easy_setopt(curlPOST_, CURLOPT_TIMEOUT, 30L); + curl_easy_setopt(curlPOST_, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curlPOST_, CURLOPT_VERBOSE, 0L); // debug option + curl_easy_setopt(curlPOST_, CURLOPT_WRITEDATA, &result); + curl_easy_setopt(curlPOST_, CURLOPT_WRITEFUNCTION, WriteCallback); - res_ = curl_easy_perform(curl_); + curlStatusCode_ = curl_easy_perform(curlPOST_); // libcurl internal error handling - if (res_ != CURLE_OK) + if (curlStatusCode_ != CURLE_OK) { cout << "Libcurl error in DoPOSTrequest(), code:\n"; - return res_; + return curlStatusCode_; } - return res_; + return curlStatusCode_; } else From 24f185b05ba5ba279afb97bb89ad4d977e60478a Mon Sep 17 00:00:00 2001 From: Maple Date: Thu, 28 Jun 2018 16:13:34 +0200 Subject: [PATCH 08/43] code condense cleanup optimization achitecture refactor --- README.md | 24 +- example.cpp | 218 ++++++++-------- example2.cpp | 63 ----- include/BitfinexAPI.hpp | 540 +++++++++++++++++++++------------------- include/jsonutils.hpp | 5 +- 5 files changed, 411 insertions(+), 439 deletions(-) delete mode 100644 example2.cpp diff --git a/README.md b/README.md index 68ca8af..9d640f4 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ _C++ Bitfinex REST API client_ ### Synopsis -This header-only library contains class for interfacing Bitfinex REST API v1 +This header-only library contains class for interfacing Bitfinex REST API v1. ### Installation @@ -15,7 +15,27 @@ add `#include "BitfinexAPI.hpp"` to your `.cpp` file. ### Usage -See self-explanatory `example.cpp` for general usage. + // Create API client for both authenticated and unauthenticated requests + BfxAPI::BitfinexAPI bfxAPI("accessKey", "secretKey"); + + // Create API client for just unauthenticated requests + BfxAPI::BitfinexAPI bfxAPI(); + + // Fetch data + bfxAPI.getTicker("btcusd"); + + // Check for errors + if (!bfxAPI.hasApiError()) + // Get response in string + cout << bfxAPI.strResponse() << endl; + else + { + // Inspect errors + cout << bfxAPI.getBfxApiStatusCode() << endl; + cout << bfxAPI.getCurlStatusCode() << endl; + } + +See self-explanatory `example.cpp` for general usage and more requests. ### Dependencies diff --git a/example.cpp b/example.cpp index 2626485..b188486 100644 --- a/example.cpp +++ b/example.cpp @@ -1,11 +1,11 @@ -////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // // example.cpp // // // Bitfinex REST API C++ client - examples // -////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // std #include @@ -21,126 +21,122 @@ using std::endl; using std::ifstream; using std::string; -using BfxAPI::BitfinexAPI; - int main(int argc, char *argv[]) { - ///////////////////////////////////////////////////////////////////////// - // Examples - // Note that default values are not mandatory. See BitfinexAPI.hpp - // for details. - ///////////////////////////////////////////////////////////////////////// - - ///////////////////////////////////////////////////////////////////////// - // GET REQUESTS - unauthenticated endpoints - ///////////////////////////////////////////////////////////////////////// - - BitfinexAPI bfxAPI; - string response; - int errCode = 0; - - // errCode = bfxAPI.getTicker(response, "btcusd"); - // errCode = bfxAPI.getStats(response, "btcusd"); - // errCode = bfxAPI.getFundingBook(response, "USD", 50, 50); - // errCode = bfxAPI.getOrderBook(response, "btcusd", 50, 50, 1); - // errCode = bfxAPI.getTrades(response, "btcusd", 0L, 50); - // errCode = bfxAPI.getLends(response, "USD", 0L, 50); - errCode = bfxAPI.getSymbols(response); - // errCode = bfxAPI.getSymbolDetails(response); - - cout << "Response: " << response << endl; - cout << "Error code: " << errCode << endl; - - ///////////////////////////////////////////////////////////////////////// - // POST REQUESTS - authenticated endpoints - ///////////////////////////////////////////////////////////////////////// + // Create bfxAPI without API keys + BfxAPI::BitfinexAPI bfxAPI; + // Create bfxAPI with API keys + // BfxAPI::BitfinexAPI bfxAPI("accessKey", "secretKey"); + // Load API keys from file ifstream ifs("key-secret", ifstream::in); if (!ifs.is_open()) { cout << "Can't open 'key-secret' file. " << endl; return 1; } + getline(ifs, bfxAPI.setAccessKey()); + getline(ifs, bfxAPI.setSecretKey()); + ifs.close(); + + // Fetch API + cout << "Request with error checking: " << endl; + bfxAPI.getTicker("btcusd"); + if (!bfxAPI.hasApiError()) + cout << bfxAPI.strResponse() << endl; else { - response.clear(); - string accessKey, secretKey; - getline(ifs, accessKey); - getline(ifs, secretKey); - ifs.close(); - - bfxAPI.setKeys(accessKey, secretKey); - - /// Account /// - // errCode = bfxAPI.getAccountInfo(response); - // errCode = bfxAPI.getAccountFees(response); - // errCode = bfxAPI.getSummary(response); - // errCode = bfxAPI.deposit(response, "bitcoin", "deposit", 1); - // errCode = bfxAPI.getKeyPermissions(response); - // errCode = bfxAPI.getMarginInfos(response); - // errCode = bfxAPI.getBalances(response); - // errCode = bfxAPI.transfer(response, 0.1, "BTC", "trading", "deposit"); - // errCode = bfxAPI.withdraw(response); // configure withdraw.conf file before use - - /// Orders /// - // errCode = bfxAPI.newOrder(response, "btcusd", 0.01, 983, "sell", "exchange limit", 0, 1, - // 0, 0, 0); - // - // How to create vOrders object for newOrders() call - // BitfinexAPI::vOrders orders = - // { - // {"btcusd", 0.1, 950, "sell", "exchange limit"}, - // {"btcusd", 0.1, 950, "sell", "exchange limit"}, - // {"btcusd", 0.1, 950, "sell", "exchange limit"} - // }; - // errCode = bfxAPI.newOrders(response, orders); - // - // errCode = bfxAPI.cancelOrder(response, 13265453586LL); - // - // How to create ids object for cancelOrders() call - // BitfinexAPI::vIds ids = - // { - // 12324589754LL, - // 12356754322LL, - // 12354996754LL - // }; - // errCode = bfxAPI.cancelOrders(response, ids); - // - // errCode = bfxAPI.cancelAllOrders(response); - // errCode = bfxAPI.replaceOrder(response, 1321548521LL, "btcusd", 0.05, 1212, "sell", - // "exchange limit", 0, 0); - // errCode = bfxAPI.getOrderStatus(response, 12113548453LL); - // errCode = bfxAPI.getActiveOrders(response); - // errCode = bfxAPI.getOrdersHistory(response, 10); - - - /// Positions /// - // errCode = bfxAPI.getActivePositions(response); - // errCode = bfxAPI.claimPosition(response, 156321412LL, 150); - - /// Historical data /// - // errCode = bfxAPI.getBalanceHistory(response, "USD", 0L, 0L, 500, "all"); - // errCode = bfxAPI.getWithdrawalHistory(response, "BTC", "all", 0L , 0L, 500); - // errCode = bfxAPI.getPastTrades(response, "btcusd", 0L, 0L, 500, 0); - - /// Margin funding /// - // errCode = bfxAPI.newOffer(response, "USD", 12000, 25.2, 30, "lend"); - // errCode = bfxAPI.cancelOffer(response, 12354245628LL); - // errCode = bfxAPI.getOfferStatus(response, 12313541215LL); - // errCode = bfxAPI.getActiveCredits(response); - // errCode = bfxAPI.getOffers(response); - // errCode = bfxAPI.getOffersHistory(response, 50); - // errCode = bfxAPI.getPastFundingTrades(response, "BTC", 0, 50); - // errCode = bfxAPI.getTakenFunds(response); - // errCode = bfxAPI.getUnusedTakenFunds(response); - // errCode = bfxAPI.getTotalTakenFunds(response); - // errCode = bfxAPI.closeLoan(response, 1235845634LL); - // errCode = bfxAPI.closePosition(response, 1235845634LL); - - cout << "Response: " << response << endl; - cout << "Error code: " << errCode << endl; - - return 0; + // see bfxERR enum in BitfinexAPI.hpp::BitfinexAPI + cout << bfxAPI.getBfxApiStatusCode() << endl; + // see https://curl.haxx.se/libcurl/c/libcurl-errors.html + cout << bfxAPI.getCurlStatusCode() << endl; } + + cout << "Request without error checking: " << endl; + cout << bfxAPI.getSummary().strResponse() << endl; + + //////////////////////////////////////////////////////////////////////////// + /// Available unauthenticated requests + //////////////////////////////////////////////////////////////////////////// + + // bfxAPI.getTicker("btcusd"); + // bfxAPI.getStats("btcusd"); + // bfxAPI.getFundingBook("USD", 50, 50); + // bfxAPI.getOrderBook("btcusd", 50, 50, 1); + // bfxAPI.getTrades("btcusd", 0L, 50); + // bfxAPI.getLends("USD", 0L, 50); + // bfxAPI.getSymbols(); + // bfxAPI.getSymbolDetails(); + + //////////////////////////////////////////////////////////////////////////// + /// Available authenticated requests + //////////////////////////////////////////////////////////////////////////// + + /// Account /// + // bfxAPI.getAccountInfo(); + // bfxAPI.getAccountFees(); + // bfxAPI.getSummary(); + // bfxAPI.deposit("bitcoin", "deposit", 1); + // bfxAPI.getKeyPermissions(); + // bfxAPI.getMarginInfos(); + // bfxAPI.getBalances(); + // bfxAPI.transfer(0.1, "BTC", "trading", "deposit"); + // bfxAPI.withdraw(); // configure withdraw.conf file before use + + /// Orders /// + // bfxAPI.newOrder("btcusd", 0.01, 983, "sell", "exchange limit", 0, 1, + // 0, 0, 0); + // + // How to create vOrders object for newOrders() call + // BitfinexAPI::vOrders orders = + // { + // {"btcusd", 0.1, 950, "sell", "exchange limit"}, + // {"btcusd", 0.1, 950, "sell", "exchange limit"}, + // {"btcusd", 0.1, 950, "sell", "exchange limit"} + // }; + // bfxAPI.newOrders(orders); + // + // bfxAPI.cancelOrder(13265453586LL); + // + // How to create ids object for cancelOrders() call + // BitfinexAPI::vIds ids = + // { + // 12324589754LL, + // 12356754322LL, + // 12354996754LL + // }; + // bfxAPI.cancelOrders(ids); + // + // bfxAPI.cancelAllOrders(); + // bfxAPI.replaceOrder(1321548521LL, "btcusd", 0.05, 1212, "sell", + // "exchange limit", 0, 0); + // bfxAPI.getOrderStatus(12113548453LL); + // bfxAPI.getActiveOrders(); + // bfxAPI.getOrdersHistory(10); + + /// Positions /// + // bfxAPI.getActivePositions(); + // bfxAPI.claimPosition(156321412LL, 150); + + /// Historical data /// + // bfxAPI.getBalanceHistory("USD", 0L, 0L, 500, "all"); + // bfxAPI.getWithdrawalHistory("BTC", "all", 0L , 0L, 500); + // bfxAPI.getPastTrades("btcusd", 0L, 0L, 500, 0); + + /// Margin funding /// + // bfxAPI.newOffer("USD", 12000, 25.2, 30, "lend"); + // bfxAPI.cancelOffer(12354245628LL); + // bfxAPI.getOfferStatus(12313541215LL); + // bfxAPI.getActiveCredits(); + // bfxAPI.getOffers(); + // bfxAPI.getOffersHistory(50); + // bfxAPI.getPastFundingTrades("BTC", 0, 50); + // bfxAPI.getTakenFunds(); + // bfxAPI.getUnusedTakenFunds(); + // bfxAPI.getTotalTakenFunds(); + // bfxAPI.closeLoan(1235845634LL); + // bfxAPI.closePosition(1235845634LL); + + return 0; } diff --git a/example2.cpp b/example2.cpp deleted file mode 100644 index 4e8d7cc..0000000 --- a/example2.cpp +++ /dev/null @@ -1,63 +0,0 @@ -////////////////////////////////////////////////////////////////////////////// -// -// example.cpp -// -// -// Bitfinex REST API C++ client - examples -// -////////////////////////////////////////////////////////////////////////////// - -// std -#include -#include - -// BitfinexAPI -#include "BitfinexAPI.hpp" - - -// namespaces -using std::cout; -using std::endl; -using std::ifstream; -using std::string; - - -int main(int argc, char *argv[]) -{ - ///////////////////////////////////////////////////////////////////////// - // Examples - // Note that default values are not mandatory. See BitfinexAPI.hpp - // for details. - ///////////////////////////////////////////////////////////////////////// - - ///////////////////////////////////////////////////////////////////////// - // GET REQUESTS - unauthenticated endpoints - ///////////////////////////////////////////////////////////////////////// - - BfxAPI::BitfinexAPI bfxAPI; - - bfxAPI.getTicker("btcusd"); - if (!bfxAPI.hasApiError()) - cout << bfxAPI.strResponse() << endl; - else - { - // see bfxERR enum in BitfinexAPI.hpp::BitfinexAPI - cout << bfxAPI.getBfxApiStatusCode() << endl; - // see https://curl.haxx.se/libcurl/c/libcurl-errors.html - cout << bfxAPI.getCurlStatusCode() << endl; - } - - bfxAPI.getStats("btcusd"); - if (!bfxAPI.hasApiError()) - cout << bfxAPI.strResponse() << endl; - else - { - // see bfxERR enum in BitfinexAPI.hpp::BitfinexAPI - cout << bfxAPI.getBfxApiStatusCode() << endl; - // see https://curl.haxx.se/libcurl/c/libcurl-errors.html - cout << bfxAPI.getCurlStatusCode() << endl; - } - - return 0; - -} diff --git a/include/BitfinexAPI.hpp b/include/BitfinexAPI.hpp index 7dda72c..9114a30 100644 --- a/include/BitfinexAPI.hpp +++ b/include/BitfinexAPI.hpp @@ -76,16 +76,16 @@ namespace BfxAPI enum bfxERR { noError = 0, - curlERR, - badSymbol, - badCurrency, - badDepositMethod, - badWalletType, - requiredParamsMissing, - wireParamsMissing, - addressParamsMissing, - badOrderType, - jsonStrToUSetError + curlERR, // 1 + badSymbol, // 2 + badCurrency, // 3 + badDepositMethod, // 4 + badWalletType, // 5 + requiredParamsMissing, // 6 + wireParamsMissing, // 7 + addressParamsMissing, // 8 + badOrderType, // 9 + jsonStrToUSetError // 10 }; //////////////////////////////////////////////////////////////////////// @@ -194,11 +194,11 @@ namespace BfxAPI //////////////////////////////////////////////////////////////////////// // Getters - string getWDconfFilePath() const { return WDconfFilePath_; } - int getBfxApiStatusCode() const { return bfxApiStatusCode_; } - int getCurlStatusCode() const { return curlStatusCode_; } + const string getWDconfFilePath() const { return WDconfFilePath_; } + const bfxERR& getBfxApiStatusCode() const { return bfxApiStatusCode_; } + const CURLcode& getCurlStatusCode() const { return curlStatusCode_; } const string& strResponse() const { return result_ ; } - bool hasApiError() const + bool hasApiError() const { if (bfxApiStatusCode_ == noError && curlStatusCode_ == CURLE_OK) return false; @@ -210,9 +210,11 @@ namespace BfxAPI void setWDconfFilePath(const string &path) { WDconfFilePath_ = path; } void setKeys(const string &accessKey, const string &secretKey) { - this->accessKey_ = accessKey; - this->secretKey_ = secretKey; + accessKey_ = accessKey; + secretKey_ = secretKey; } + string& setAccessKey() { return accessKey_;} + string& setSecretKey() { return secretKey_;} //////////////////////////////////////////////////////////////////////// // Public endpoints @@ -239,8 +241,8 @@ namespace BfxAPI }; BitfinexAPI& getFundingBook(const string ¤cy, - const int &limit_bids = 50, - const int &limit_asks = 50) + const unsigned &limit_bids = 50, + const unsigned &limit_asks = 50) { if (!inArray(currency, currencies_)) bfxApiStatusCode_ = badCurrency; @@ -256,9 +258,9 @@ namespace BfxAPI }; BitfinexAPI& getOrderBook(const string &symbol, - const int &limit_bids = 50, - const int &limit_asks = 50, - const bool &group = 1) + const unsigned &limit_bids = 50, + const unsigned &limit_asks = 50, + const bool &group = true) { if (!inArray(symbol, symbols_)) bfxApiStatusCode_ = badSymbol; @@ -276,7 +278,7 @@ namespace BfxAPI BitfinexAPI& getTrades(const string &symbol, const time_t &since = 0, - const int &limit_trades = 50) + const unsigned &limit_trades = 50) { if (!inArray(symbol, symbols_)) bfxApiStatusCode_ = badSymbol; @@ -293,7 +295,7 @@ namespace BfxAPI BitfinexAPI& getLends(const string ¤cy, const time_t &since = 0, - const int &limit_lends = 50) + const unsigned &limit_lends = 50) { if (!inArray(currency, currencies_)) bfxApiStatusCode_ = badCurrency; @@ -315,7 +317,7 @@ namespace BfxAPI return *this; }; - BitfinexAPI& getSymbolDetails() + BitfinexAPI& getSymbolsDetails() { DoGETrequest("/symbols_details/", ""); @@ -327,45 +329,45 @@ namespace BfxAPI //////////////////////////////////////////////////////////////////////// // Account - int getAccountInfo(string &result) + BitfinexAPI& getAccountInfo() { string params = "{\"request\":\"/v1/account_infos\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - return DoPOSTrequest("/account_infos/", params, result); + DoPOSTrequest("/account_infos/", params); + + return *this; }; - int getAccountFees(string &result) + BitfinexAPI& getAccountFees() { string params = "{\"request\":\"/v1/account_fees\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - return DoPOSTrequest("/account_fees/", params, result); + DoPOSTrequest("/account_fees/", params); + + return *this; }; - int getSummary(string &result) + BitfinexAPI& getSummary() { string params = "{\"request\":\"/v1/summary\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - return DoPOSTrequest("/summary/", params, result); + DoPOSTrequest("/summary/", params); + + return *this; }; - int deposit(string &result, - const string &method, - const string &walletName, - const bool &renew = 0) + BitfinexAPI& deposit(const string &method, + const string &walletName, + const bool &renew = false) { - // Is deposit method valid ? - if(!inArray(method, methods_)) - { - return badDepositMethod; - } - // Is walletType valid ? - if(!inArray(walletName, walletNames_)) - { - return badWalletType; - } + if (!inArray(method, methods_)) + { bfxApiStatusCode_ = badDepositMethod; return *this; } + + if (!inArray(walletName, walletNames_)) + { bfxApiStatusCode_ = badWalletType; return *this; } string params = "{\"request\":\"/v1/deposit/new\",\"nonce\":\"" + getTonce() + "\""; @@ -373,50 +375,52 @@ namespace BfxAPI params += ",\"wallet_name\":\"" + walletName + "\""; params += ",\"renew\":" + to_string(renew); params += "}"; - return DoPOSTrequest("/deposit/new/", params, result); + DoPOSTrequest("/deposit/new/", params); + + return *this; }; - int getKeyPermissions(string &result) + BitfinexAPI& getKeyPermissions() { string params = "{\"request\":\"/v1/key_info\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - return DoPOSTrequest("/key_info/", params, result); + DoPOSTrequest("/key_info/", params); + + return *this; }; - int getMarginInfos(string &result) + BitfinexAPI& getMarginInfos() { string params = "{\"request\":\"/v1/margin_infos\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - return DoPOSTrequest("/margin_infos/", params, result); + DoPOSTrequest("/margin_infos/", params); + + return *this; }; - int getBalances(string &result) + BitfinexAPI& getBalances() { string params = "{\"request\":\"/v1/balances\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - return DoPOSTrequest("/balances/", params, result); + DoPOSTrequest("/balances/", params); + + return *this; }; - int transfer(string &result, - const double &amount, - const string ¤cy, - const string &walletfrom, - const string &walletto) + BitfinexAPI& transfer(const double &amount, + const string ¤cy, + const string &walletfrom, + const string &walletto) { - // Is currency valid ? - if(!inArray(currency, currencies_)) - { - return badCurrency; - } - // Is walletType valid ? - if(!inArray(walletfrom, walletNames_) || + if (!inArray(currency, currencies_)) + { bfxApiStatusCode_ = badCurrency; return *this; } + + if (!inArray(walletfrom, walletNames_) || !inArray(walletto, walletNames_)) - { - return badWalletType; - } + { bfxApiStatusCode_ = badWalletType; return *this; } string params = "{\"request\":\"/v1/transfer\",\"nonce\":\"" + getTonce() + "\""; @@ -425,45 +429,47 @@ namespace BfxAPI params += ",\"walletfrom\":\"" + walletfrom + "\""; params += ",\"walletto\":\"" + walletto + "\""; params += "}"; - return DoPOSTrequest("/transfer/", params, result); + DoPOSTrequest("/transfer/", params); + + return *this; }; - int withdraw(string &result) // configure withdraw.conf file before use + // configure withdraw.conf file before use + BitfinexAPI& withdraw() { string params = "{\"request\":\"/v1/withdraw\",\"nonce\":\"" + getTonce() + "\""; // Add params from withdraw.conf - int err = parseWDconfParams(params); - if (err != 0) { return err; }; + bfxERR code(parseWDconfParams(params)); + if (code != noError) + bfxApiStatusCode_ = code; + else + { + params += "}"; + DoPOSTrequest("/withdraw/", params); + } - params += "}"; - return DoPOSTrequest("/withdraw/", params, result); + return *this; }; // Orders - int newOrder(string &result, - const string &symbol, - const double &amount, - const double &price, - const string &side, - const string &type, - const bool &is_hidden = 0, - const bool &is_postonly = 0, - const bool &use_all_available = 0, - const bool &ocoorder = 0, - const double &buy_price_oco = 0) + BitfinexAPI& newOrder(const string &symbol, + const double &amount, + const double &price, + const string &side, + const string &type, + const bool &is_hidden = false, + const bool &is_postonly = false, + const bool &use_all_available = false, + const bool &ocoorder = false, + const double &buy_price_oco = 0) { - // Is symbol valid ? - if(!inArray(symbol, symbols_)) - { - return badSymbol; - } - // Is order type valid ? - if(!inArray(type, types_)) - { - return badOrderType; - } + if (!inArray(symbol, symbols_)) + { bfxApiStatusCode_ = badSymbol; return *this; }; + + if (!inArray(type, types_)) + { bfxApiStatusCode_ = badOrderType; return *this; }; string params = "{\"request\":\"/v1/order/new\",\"nonce\":\"" + getTonce() + "\""; @@ -478,10 +484,12 @@ namespace BfxAPI params += ",\"ocoorder\":" + bool2string(ocoorder); params += ",\"buy_price_oco\":" + bool2string(buy_price_oco); params += "}"; - return DoPOSTrequest("/order/new/", params, result); + + DoPOSTrequest("/order/new/", params); + return *this; }; - int newOrders(string &result, const vOrders &orders) + BitfinexAPI& newOrders(const vOrders &orders) { string params = "{\"request\":\"/v1/order/new/multi\",\"nonce\":\"" + getTonce() + "\""; @@ -499,75 +507,71 @@ namespace BfxAPI params += ",\"side\":\"" + order.side + "\""; params += ",\"type\":\"" + order.type + "\"}"; if (&order != &last) - { params += ","; - } } - params += "]"; - params += "}"; - return DoPOSTrequest("/order/new/multi/", params, result); + params += "]}"; + DoPOSTrequest("/order/new/multi/", params); + + return *this; }; - int cancelOrder(string &result, const long long &order_id) + BitfinexAPI& cancelOrder(const long long &order_id) { string params = "{\"request\":\"/v1/order/cancel\",\"nonce\":\"" + getTonce() + "\""; params += ",\"order_id\":" + to_string(order_id); params += "}"; - return DoPOSTrequest("/order/cancel/", params, result); + DoPOSTrequest("/order/cancel/", params); + + return *this; }; - int cancelOrders(string &result, const vIds &vOrder_ids) + BitfinexAPI& cancelOrders(const vIds &vOrderIds) { string params = "{\"request\":\"/v1/order/cancel/multi\",\"nonce\":\"" + getTonce() + "\""; // Get pointer to last element in vOrders. We will not place // ',' character at the end of the last loop. - auto &last = *(--vOrder_ids.cend()); + auto &last = *(--vOrderIds.cend()); params += ", \"order_ids\":["; - for (const auto &order_id : vOrder_ids) + for (const auto &order_id : vOrderIds) { params += to_string(order_id); if (&order_id != &last) - { params += ","; - } } - params += "]"; - params += "}"; - return DoPOSTrequest("/order/cancel/multi/", params, result); + params += "]}"; + DoPOSTrequest("/order/cancel/multi/", params); + + return *this; }; - int cancelAllOrders(string &result) + BitfinexAPI& cancelAllOrders() { string params = "{\"request\":\"/v1/order/cancel/all\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - return DoPOSTrequest("/order/cancel/all/", params, result); + DoPOSTrequest("/order/cancel/all/", params); + + return *this; }; - int replaceOrder(string &result, - const long long &order_id, - const string &symbol, - const double &amount, - const double &price, - const string &side, - const string &type, - const bool &is_hidden = 0, - const bool &use_remaining = 0) + BitfinexAPI& replaceOrder(const long long &order_id, + const string &symbol, + const double &amount, + const double &price, + const string &side, + const string &type, + const bool &is_hidden = false, + const bool &use_remaining = false) { - // Is symbol valid ? - if(!inArray(symbol, symbols_)) - { - return badSymbol; - } - // Is order type valid ? - if(!inArray(type, types_)) - { - return badOrderType; - } + if (!inArray(symbol, symbols_)) + { bfxApiStatusCode_ = badSymbol; return *this; }; + + if (!inArray(type, types_)) + { bfxApiStatusCode_ = badOrderType; return *this; }; string params = "{\"request\":\"/v1/order/cancel/replace\",\"nonce\":\"" + getTonce() + "\""; @@ -580,77 +584,86 @@ namespace BfxAPI params += ",\"is_hidden\":" + bool2string(is_hidden); params += ",\"use_all_available\":" + bool2string(use_remaining); params += "}"; - return DoPOSTrequest("/order/cancel/replace/", params, result); + DoPOSTrequest("/order/cancel/replace/", params); + + return *this; }; - int getOrderStatus(string &result, const long long &order_id) + BitfinexAPI& getOrderStatus(const long long &order_id) { string params = "{\"request\":\"/v1/order/status\",\"nonce\":\"" + getTonce() + "\""; params += ",\"order_id\":" + to_string(order_id); params += "}"; - return DoPOSTrequest("/order/status/", params, result); + DoPOSTrequest("/order/status/", params); + + return *this; }; - int getActiveOrders(string &result) + BitfinexAPI& getActiveOrders() { string params = "{\"request\":\"/v1/orders\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - return DoPOSTrequest("/orders/", params, result); + DoPOSTrequest("/orders/", params); + + return *this; }; - int getOrdersHistory(string &result, const int &limit = 50) + BitfinexAPI& getOrdersHistory(const unsigned &limit = 50) { string params = "{\"request\":\"/v1/orders/hist\",\"nonce\":\"" + getTonce() + "\""; params += ",\"limit\":" + to_string(limit); params += "}"; - return DoPOSTrequest("/orders/hist/", params, result); + DoPOSTrequest("/orders/hist/", params); + + return *this; }; // Positions - int getActivePositions(string &result) + BitfinexAPI& getActivePositions() { string params = "{\"request\":\"/v1/positions\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - return DoPOSTrequest("/positions/", params, result); + DoPOSTrequest("/positions/", params); + + return *this; }; - int claimPosition(string &result, - long long &position_id, - const double &amount) + BitfinexAPI& claimPosition(long long &position_id, + const double &amount) { string params = "{\"request\":\"/v1/position/claim\",\"nonce\":\"" + getTonce() + "\""; params += ",\"position_id\":" + to_string(position_id); params += ",\"amount\":\"" + to_string(amount) + "\""; params += "}"; - return DoPOSTrequest("/position/claim/", params, result); + DoPOSTrequest("/position/claim/", params); + + return *this; }; + // Historical data - int getBalanceHistory(string &result, - const string ¤cy, - const time_t &since = 0, - const time_t &until = 0, - const int &limit = 500, - const string &walletType = "all") + BitfinexAPI& getBalanceHistory(const string ¤cy, + const time_t &since = 0, + const time_t &until = 0, + const unsigned &limit = 500, + const string &walletType = "all") { // Is currency valid ? - if(!inArray(currency, currencies_)) - { - return badCurrency; - } + if (!inArray(currency, currencies_)) + { bfxApiStatusCode_ = badCurrency; return *this; }; + // Is wallet type valid ? // Modified condition which accepts "all" value for all wallets - // balances together. - if(!inArray(walletType, walletNames_) && walletType != "all") - { - return badWalletType; - } + // balances together.If "all" specified then there is simply no + // wallet parameter in POST request. + if (!inArray(walletType, walletNames_) || walletType != "all") + { bfxApiStatusCode_ = badWalletType; return *this; }; string params = "{\"request\":\"/v1/history\",\"nonce\":\"" + getTonce() + "\""; @@ -660,60 +673,50 @@ namespace BfxAPI + "\""; params += ",\"limit\":" + to_string(limit); if (walletType != "all") - { params += ",\"wallet\":\"" + walletType + "\""; - } - params += "}"; - return DoPOSTrequest("/history/", params, result); + DoPOSTrequest("/history/", params); + + return *this; }; - int getWithdrawalHistory(string &result, - const string ¤cy, - const string &method = "all", - const time_t &since = 0, - const time_t &until = 0, - const int &limit = 500) + BitfinexAPI& getWithdrawalHistory(const string ¤cy, + const string &method = "all", + const time_t &since = 0, + const time_t &until = 0, + const unsigned &limit = 500) { - // Is currency valid ? - if(!inArray(currency, currencies_)) - { - return badCurrency; - } - // Is deposit method valid ? - if(!inArray(method, methods_) && method != "wire" && method != "all") - { - return badDepositMethod; - } + if (!inArray(currency, currencies_)) + { bfxApiStatusCode_ = badCurrency; return *this; }; + + if (!inArray(method, methods_) && method != "wire" && method != "all") + { bfxApiStatusCode_ = badDepositMethod; return *this; }; string params = "{\"request\":\"/v1/history/movements\",\"nonce\":\"" + getTonce() + "\""; params += ",\"currency\":\"" + currency + "\""; if (method != "all") - { params += ",\"method\":\"" + method + "\""; - } params += ",\"since\":\"" + to_string(since) + "\""; params += ",\"until\":\"" + (!until ? getTonce() : to_string(until)) + "\""; params += ",\"limit\":" + to_string(limit); params += "}"; - return DoPOSTrequest("/history/movements/", params, result); + DoPOSTrequest("/history/movements/", params); + + return *this; }; - int getPastTrades(string &result, - const string &symbol, - const time_t ×tamp, - const time_t &until = 0, - const int &limit_trades = 500, - const bool reverse = 0) + BitfinexAPI& getPastTrades(const string &symbol, + const time_t ×tamp, + const time_t &until = 0, + const unsigned &limit_trades = 500, + const bool reverse = false) { - // Is symbol valid ? - if(!inArray(symbol, symbols_)) + if (!inArray(symbol, symbols_)) + bfxApiStatusCode_ = badSymbol; + else { - return badSymbol; - } - string params = "{\"request\":\"/v1/mytrades\",\"nonce\":\"" + getTonce() + "\""; params += ",\"symbol\":\"" + symbol + "\""; @@ -723,23 +726,23 @@ namespace BfxAPI params += ",\"limit_trades\":" + to_string(limit_trades); params += ",\"reverse\":" + to_string(reverse); params += "}"; - return DoPOSTrequest("/mytrades/", params, result); + DoPOSTrequest("/mytrades/", params); + } + + return *this; }; // Margin funding - int newOffer(string &result, - const string ¤cy, - const double &amount, - const float &rate, - const int &period, - const string &direction) + BitfinexAPI& newOffer(const string ¤cy, + const double &amount, + const float &rate, + const unsigned &period, + const string &direction) { - // Is currency valid ? if(!inArray(currency, currencies_)) + bfxApiStatusCode_ = badCurrency; + else { - return badCurrency; - } - string params = "{\"request\":\"/v1/offer/new\",\"nonce\":\"" + getTonce() + "\""; params += ",\"currency\":\"" + currency + "\""; @@ -748,50 +751,63 @@ namespace BfxAPI params += ",\"period\":" + to_string(period); params += ",\"direction\":\"" + direction + "\""; params += "}"; - return DoPOSTrequest("/offer/new/", params, result); + DoPOSTrequest("/offer/new/", params); + } + + return *this; }; - int cancelOffer(string &result, const long long &offer_id) + BitfinexAPI& cancelOffer(const long long &offer_id) { string params = "{\"request\":\"/v1/offer/cancel\",\"nonce\":\"" + getTonce() + "\""; params += ",\"offer_id\":" + to_string(offer_id); params += "}"; - return DoPOSTrequest("/offer/cancel/", params, result); + DoPOSTrequest("/offer/cancel/", params); + + return *this; }; - int getOfferStatus(string &result, const long long &offer_id) + BitfinexAPI& getOfferStatus(const long long &offer_id) { string params = "{\"request\":\"/v1/offer/status\",\"nonce\":\"" + getTonce() + "\""; params += ",\"offer_id\":" + to_string(offer_id); params += "}"; - return DoPOSTrequest("/offer/status/", params, result); + DoPOSTrequest("/offer/status/", params); + + return *this; }; - int getActiveCredits(string &result) + BitfinexAPI& getActiveCredits() { string params = "{\"request\":\"/v1/credits\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - return DoPOSTrequest("/credits/", params, result); + DoPOSTrequest("/credits/", params); + + return *this; }; - int getOffers(string &result) + BitfinexAPI& getOffers() { string params = "{\"request\":\"/v1/offers\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - return DoPOSTrequest("/offers/", params, result); + DoPOSTrequest("/offers/", params); + + return *this; }; - int getOffersHistory(string &result, const int &limit) + BitfinexAPI& getOffersHistory(const unsigned &limit) { string params = "{\"request\":\"/v1/offers/hist\",\"nonce\":\"" + getTonce() + "\""; params += ",\"limit\":" + to_string(limit); params += "}"; - return DoPOSTrequest("/offers/hist/", params, result); + DoPOSTrequest("/offers/hist/", params); + + return *this; }; // There is ambiguity in the "symbol" parameter value for this call. @@ -799,67 +815,78 @@ namespace BfxAPI // Typical values for "symbol" are trading pairs such as "btcusd", // "btcltc" ... // Typical values for "currency" are "btc", "ltc" ... - int getPastFundingTrades(string &result, - const string ¤cy, - const time_t &until = 0, - const int &limit_trades = 50) + BitfinexAPI& getPastFundingTrades(const string ¤cy, + const time_t &until = 0, + const unsigned &limit_trades = 50) { // Is currency valid ? if(!inArray(currency, currencies_)) + bfxApiStatusCode_ = badCurrency; + else { - return badCurrency; + string params = "{\"request\":\"/v1/mytrades_funding\",\"nonce\":\"" + + getTonce() + "\""; + // param inconsistency in BFX API, "symbol" should be currency + params += ",\"symbol\":\"" + currency + "\""; + params += ",\"until\":" + to_string(until); + params += ",\"limit_trades\":" + to_string(limit_trades); + params += "}"; + DoPOSTrequest("/mytrades_funding/", params); } - string params = "{\"request\":\"/v1/mytrades_funding\",\"nonce\":\"" - + getTonce() + "\""; - // param inconsistency in BFX API, symbol should be currency - params += ",\"symbol\":\"" + currency + "\""; - params += ",\"until\":" + to_string(until); - params += ",\"limit_trades\":" + to_string(limit_trades); - params += "}"; - return DoPOSTrequest("/mytrades_funding/", params, result); + return *this; }; - int getTakenFunds(string &result) + BitfinexAPI& getTakenFunds() { string params = "{\"request\":\"/v1/taken_funds\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - return DoPOSTrequest("/taken_funds/", params, result); + DoPOSTrequest("/taken_funds/", params); + + return *this; }; - int getUnusedTakenFunds(string &result) + BitfinexAPI& getUnusedTakenFunds() { string params = "{\"request\":\"/v1/unused_taken_funds\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - return DoPOSTrequest("/unused_taken_funds/", params, result); + DoPOSTrequest("/unused_taken_funds/", params); + + return *this; }; - int getTotalTakenFunds(string &result) + BitfinexAPI& getTotalTakenFunds() { string params = "{\"request\":\"/v1/total_taken_funds\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - return DoPOSTrequest("/total_taken_funds/", params, result); + DoPOSTrequest("/total_taken_funds/", params); + + return *this; }; - int closeLoan(string &result, const long long &offer_id) + BitfinexAPI& closeLoan(const long long &offer_id) { string params = "{\"request\":\"/v1/funding/close\",\"nonce\":\"" + getTonce() + "\""; params += ",\"swap_id\":" + to_string(offer_id); params += "}"; - return DoPOSTrequest("/funding/close/", params, result); + DoPOSTrequest("/funding/close/", params); + + return *this; }; - int closePosition(string &result, const long long &position_id) + BitfinexAPI& closePosition(const long long &position_id) { string params = "{\"request\":\"/v1/position/close\",\"nonce\":\"" + getTonce() + "\""; params += ",\"position_id\":" + to_string(position_id); params += "}"; - return DoPOSTrequest("/position/close/", params, result); + DoPOSTrequest("/position/close/", params); + + return *this; }; protected: @@ -886,7 +913,7 @@ namespace BfxAPI // Utility protected methods //////////////////////////////////////////////////////////////////////// - int parseWDconfParams(string ¶ms) + bfxERR parseWDconfParams(string ¶ms) { using std::getline; using std::ifstream; @@ -951,11 +978,13 @@ namespace BfxAPI params += param.second; } - return 0; + return noError; }; void DoGETrequest(const string &UrlEndPoint, const string ¶ms) { + bfxApiStatusCode_ = noError; + if(curlGET_) { string url = APIurl_ + UrlEndPoint + params; @@ -976,15 +1005,13 @@ namespace BfxAPI } } else - { - cout << "curl not properly initialized curl = nullptr"; - } + cout << "curl not properly initialized curlGET_ = nullptr"; }; - int DoPOSTrequest(const string &UrlEndPoint, - const string ¶ms, - string &result) + void DoPOSTrequest(const string &UrlEndPoint, const string ¶ms) { + bfxApiStatusCode_ = noError; + if(curlPOST_) { string url = APIurl_ + UrlEndPoint; @@ -1005,13 +1032,14 @@ namespace BfxAPI curl_slist_append(httpHeaders, ("X-BFX-SIGNATURE:" + signature).c_str()); + result_.clear(); curl_easy_setopt(curlPOST_, CURLOPT_HTTPHEADER, httpHeaders); curl_easy_setopt(curlPOST_, CURLOPT_POST, 1); curl_easy_setopt(curlPOST_, CURLOPT_POSTFIELDS, "\n"); curl_easy_setopt(curlPOST_, CURLOPT_TIMEOUT, 30L); curl_easy_setopt(curlPOST_, CURLOPT_URL, url.c_str()); curl_easy_setopt(curlPOST_, CURLOPT_VERBOSE, 0L); // debug option - curl_easy_setopt(curlPOST_, CURLOPT_WRITEDATA, &result); + curl_easy_setopt(curlPOST_, CURLOPT_WRITEDATA, &result_); curl_easy_setopt(curlPOST_, CURLOPT_WRITEFUNCTION, WriteCallback); curlStatusCode_ = curl_easy_perform(curlPOST_); @@ -1019,17 +1047,13 @@ namespace BfxAPI // libcurl internal error handling if (curlStatusCode_ != CURLE_OK) { - cout << "Libcurl error in DoPOSTrequest(), code:\n"; - return curlStatusCode_; + cout << "Libcurl error in DoPOSTrequest():\n"; + cout << "CURLcode: " << curlStatusCode_ << "\n"; } - return curlStatusCode_; } else - { - // curl not properly initialized curl = NULL - return curlERR; - } + cout << "curl not properly initialized curlPOST_ = nullptr"; }; //////////////////////////////////////////////////////////////////////// @@ -1037,9 +1061,7 @@ namespace BfxAPI //////////////////////////////////////////////////////////////////////// static string bool2string(const bool &in) - { - return in ? "true" : "false"; - }; + { return in ? "true" : "false"; }; static string getTonce() { @@ -1113,8 +1135,6 @@ namespace BfxAPI static bool inArray(const string &value, const unordered_set &inputSet) - { - return (inputSet.find(value) != inputSet.cend()) ? true : false; - }; + { return (inputSet.find(value) != inputSet.cend()) ? true : false; }; }; } diff --git a/include/jsonutils.hpp b/include/jsonutils.hpp index 3308ac8..f9060fc 100644 --- a/include/jsonutils.hpp +++ b/include/jsonutils.hpp @@ -19,7 +19,6 @@ #include #include - // namespaces using std::cerr; using std::cout; @@ -115,7 +114,7 @@ namespace jsonutils } state_; }; - int jsonStrToUset(unordered_set &uSet, string &jsonStr) + unsigned jsonStrToUset(unordered_set &uSet, string &jsonStr) { // Create schema $ref resolver rj::Document sd; @@ -155,7 +154,7 @@ namespace jsonutils validator.GetInvalidDocumentPointer().StringifyUriFragment(sb); cout << "Invalid document: " << sb.GetString() << endl; } - return -10; + return 10; } else { From 66cb867415dd8f6a656131585dd8d652ce2d3b87 Mon Sep 17 00:00:00 2001 From: Maple Date: Thu, 28 Jun 2018 23:32:27 +0200 Subject: [PATCH 09/43] noexcept optmization --- include/BitfinexAPI.hpp | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/include/BitfinexAPI.hpp b/include/BitfinexAPI.hpp index 9114a30..048a34c 100644 --- a/include/BitfinexAPI.hpp +++ b/include/BitfinexAPI.hpp @@ -85,7 +85,8 @@ namespace BfxAPI wireParamsMissing, // 7 addressParamsMissing, // 8 badOrderType, // 9 - jsonStrToUSetError // 10 + jsonStrToUSetError, // 10 + badWDconfFilePath // 11 }; //////////////////////////////////////////////////////////////////////// @@ -194,11 +195,19 @@ namespace BfxAPI //////////////////////////////////////////////////////////////////////// // Getters - const string getWDconfFilePath() const { return WDconfFilePath_; } - const bfxERR& getBfxApiStatusCode() const { return bfxApiStatusCode_; } - const CURLcode& getCurlStatusCode() const { return curlStatusCode_; } - const string& strResponse() const { return result_ ; } - bool hasApiError() const + const string getWDconfFilePath() const noexcept + { return WDconfFilePath_; } + + const bfxERR& getBfxApiStatusCode() const noexcept + { return bfxApiStatusCode_; } + + const CURLcode& getCurlStatusCode() const noexcept + { return curlStatusCode_; } + + const string& strResponse() const noexcept + { return result_ ; } + + bool hasApiError() const noexcept { if (bfxApiStatusCode_ == noError && curlStatusCode_ == CURLE_OK) return false; @@ -207,8 +216,9 @@ namespace BfxAPI } // Setters - void setWDconfFilePath(const string &path) { WDconfFilePath_ = path; } - void setKeys(const string &accessKey, const string &secretKey) + void setWDconfFilePath(const string &path) noexcept + { WDconfFilePath_ = path; } + void setKeys(const string &accessKey, const string &secretKey) noexcept { accessKey_ = accessKey; secretKey_ = secretKey; @@ -923,9 +933,12 @@ namespace BfxAPI using std::smatch; string line; - ifstream inFile; map mParams; - inFile.open(WDconfFilePath_); + ifstream inFile(WDconfFilePath_, ifstream::in); + if (!inFile.is_open()) + { + return badWDconfFilePath; + } regex rgx("^(.*)\\b\\s*=\\s*(\"{0,1}.*\"{0,1})$"); smatch match; @@ -1060,10 +1073,10 @@ namespace BfxAPI // Utility private static methods //////////////////////////////////////////////////////////////////////// - static string bool2string(const bool &in) + static string bool2string(const bool &in) noexcept { return in ? "true" : "false"; }; - static string getTonce() + static string getTonce() noexcept { using namespace std::chrono; @@ -1127,14 +1140,14 @@ namespace BfxAPI static size_t WriteCallback(void *response, size_t size, size_t nmemb, - void *userp) + void *userp) noexcept { (static_cast(userp))->append(static_cast(response)); return size * nmemb; }; static bool inArray(const string &value, - const unordered_set &inputSet) + const unordered_set &inputSet) noexcept { return (inputSet.find(value) != inputSet.cend()) ? true : false; }; }; } From 43bdbf5eed0170c2afbf5b3cceb14cc737d18112 Mon Sep 17 00:00:00 2001 From: Maple Date: Fri, 29 Jun 2018 13:40:56 +0200 Subject: [PATCH 10/43] constexpr optimization --- include/BitfinexAPI.hpp | 14 ++++++++------ include/jsonutils.hpp | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/include/BitfinexAPI.hpp b/include/BitfinexAPI.hpp index 048a34c..90e2498 100644 --- a/include/BitfinexAPI.hpp +++ b/include/BitfinexAPI.hpp @@ -207,7 +207,7 @@ namespace BfxAPI const string& strResponse() const noexcept { return result_ ; } - bool hasApiError() const noexcept + bool hasApiError() const noexcept { if (bfxApiStatusCode_ == noError && curlStatusCode_ == CURLE_OK) return false; @@ -216,15 +216,17 @@ namespace BfxAPI } // Setters - void setWDconfFilePath(const string &path) noexcept + constexpr void setWDconfFilePath(const string &path) noexcept { WDconfFilePath_ = path; } - void setKeys(const string &accessKey, const string &secretKey) noexcept + + constexpr void setKeys(const string &accessKey, const string &secretKey) noexcept { accessKey_ = accessKey; secretKey_ = secretKey; } - string& setAccessKey() { return accessKey_;} - string& setSecretKey() { return secretKey_;} + + constexpr string& setAccessKey() { return accessKey_;} + constexpr string& setSecretKey() { return secretKey_;} //////////////////////////////////////////////////////////////////////// // Public endpoints @@ -1073,7 +1075,7 @@ namespace BfxAPI // Utility private static methods //////////////////////////////////////////////////////////////////////// - static string bool2string(const bool &in) noexcept + const static string bool2string(const bool &in) noexcept { return in ? "true" : "false"; }; static string getTonce() noexcept diff --git a/include/jsonutils.hpp b/include/jsonutils.hpp index f9060fc..d40b6a2 100644 --- a/include/jsonutils.hpp +++ b/include/jsonutils.hpp @@ -74,7 +74,7 @@ namespace jsonutils jsonStrToUsetHandler():state_(State::kExpectArrayStart) {} // SAX events handlers - bool StartArray() + bool StartArray() noexcept { switch (state_) { @@ -98,7 +98,7 @@ namespace jsonutils } } - bool EndArray(rj::SizeType) + bool EndArray(rj::SizeType) noexcept { return state_ == State::kExpectValueOrArrayEnd; } From c2260e5f7afd3e61479c0d6f5d8595b7dc430ae3 Mon Sep 17 00:00:00 2001 From: Maple Date: Sat, 30 Jun 2018 14:25:03 +0200 Subject: [PATCH 11/43] minor cleanup --- include/BitfinexAPI.hpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/include/BitfinexAPI.hpp b/include/BitfinexAPI.hpp index 90e2498..c1e1055 100644 --- a/include/BitfinexAPI.hpp +++ b/include/BitfinexAPI.hpp @@ -106,7 +106,7 @@ namespace BfxAPI using vIds = vector; //////////////////////////////////////////////////////////////////////// - // Constructor - destructor + // Constructor - Destructor //////////////////////////////////////////////////////////////////////// explicit BitfinexAPI():BitfinexAPI("", "") {} @@ -180,9 +180,13 @@ namespace BfxAPI }; } - // BitfinexAPI object cannot be copied + // BitfinexAPI object cannot be + // copied BitfinexAPI(const BitfinexAPI&) = delete; BitfinexAPI& operator = (const BitfinexAPI&) = delete; + // moved + BitfinexAPI(BitfinexAPI&&) = delete; + BitfinexAPI& operator = (BitfinexAPI&&) = delete; ~BitfinexAPI() { @@ -901,11 +905,11 @@ namespace BfxAPI return *this; }; - protected: + private: - ///////////////////////////////////////////////////////////////////////// - // Protected attributes - ///////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////// + // Private attributes + //////////////////////////////////////////////////////////////////////// unordered_set symbols_; // valid symbol pairs unordered_set currencies_; // valid currencies @@ -922,7 +926,7 @@ namespace BfxAPI string result_; //////////////////////////////////////////////////////////////////////// - // Utility protected methods + // Utility private methods //////////////////////////////////////////////////////////////////////// bfxERR parseWDconfParams(string ¶ms) From cec762055c29a359988b996935f024b11b27bea3 Mon Sep 17 00:00:00 2001 From: Maple Date: Mon, 2 Jul 2018 09:55:32 +0200 Subject: [PATCH 12/43] global variables --- example.cpp | 9 +-- include/BitfinexAPI.hpp | 130 +++++++++++++++++++++------------------- include/jsonutils.hpp | 1 + 3 files changed, 76 insertions(+), 64 deletions(-) diff --git a/example.cpp b/example.cpp index b188486..ebc38c5 100644 --- a/example.cpp +++ b/example.cpp @@ -16,6 +16,7 @@ // namespaces +using std::cerr; using std::cout; using std::endl; using std::ifstream; @@ -33,18 +34,18 @@ int main(int argc, char *argv[]) ifstream ifs("key-secret", ifstream::in); if (!ifs.is_open()) { - cout << "Can't open 'key-secret' file. " << endl; + cerr << "Can't open 'key-secret' file. " << endl; return 1; } - getline(ifs, bfxAPI.setAccessKey()); - getline(ifs, bfxAPI.setSecretKey()); + getline(ifs, bfxAPI.getAccessKey()); + getline(ifs, bfxAPI.getSecretKey()); ifs.close(); // Fetch API cout << "Request with error checking: " << endl; bfxAPI.getTicker("btcusd"); if (!bfxAPI.hasApiError()) - cout << bfxAPI.strResponse() << endl; + cerr << bfxAPI.strResponse() << endl; else { // see bfxERR enum in BitfinexAPI.hpp::BitfinexAPI diff --git a/include/BitfinexAPI.hpp b/include/BitfinexAPI.hpp index c1e1055..18fa967 100644 --- a/include/BitfinexAPI.hpp +++ b/include/BitfinexAPI.hpp @@ -49,6 +49,7 @@ // namespaces +using std::cerr; using std::cout; using std::endl; using std::string; @@ -65,6 +66,16 @@ using CryptoPP::byte; namespace BfxAPI { + //////////////////////////////////////////////////////////////////////// + // Global variables + //////////////////////////////////////////////////////////////////////// + + const auto API_URL = "https://api.bitfinex.com/v1"; + const auto CURL_TIMEOUT = 30L; + const auto CURL_DEBUG_VERBOSE = 0L; + const auto WITHDRAWAL_CONF_FILE_PATH = "doc/withdraw.conf"; + + class BitfinexAPI { public: @@ -114,8 +125,8 @@ namespace BfxAPI explicit BitfinexAPI(const string &accessKey, const string &secretKey): accessKey_(accessKey), secretKey_(secretKey), - WDconfFilePath_("doc/withdraw.conf"), - APIurl_("https://api.bitfinex.com/v1"), + WDconfFilePath_(WITHDRAWAL_CONF_FILE_PATH), + APIurl_(API_URL), curlGET_(curl_easy_init()), curlPOST_(curl_easy_init()), curlStatusCode_(CURLE_OK), @@ -229,8 +240,8 @@ namespace BfxAPI secretKey_ = secretKey; } - constexpr string& setAccessKey() { return accessKey_;} - constexpr string& setSecretKey() { return secretKey_;} + constexpr string& getAccessKey() { return accessKey_;} + constexpr string& getSecretKey() { return secretKey_;} //////////////////////////////////////////////////////////////////////// // Public endpoints @@ -241,7 +252,7 @@ namespace BfxAPI if (!inArray(symbol, symbols_)) bfxApiStatusCode_ = badSymbol; else - DoGETrequest("/pubticker/" + symbol, ""); + doGETrequest("/pubticker/" + symbol, ""); return *this; }; @@ -251,7 +262,7 @@ namespace BfxAPI if (!inArray(symbol, symbols_)) bfxApiStatusCode_ = badSymbol; else - DoGETrequest("/stats/" + symbol, ""); + doGETrequest("/stats/" + symbol, ""); return *this; }; @@ -267,7 +278,7 @@ namespace BfxAPI string params = "?limit_bids=" + to_string(limit_bids) + "&limit_asks=" + to_string(limit_asks); - DoGETrequest("/lendbook/" + currency, params); + doGETrequest("/lendbook/" + currency, params); } return *this; @@ -286,7 +297,7 @@ namespace BfxAPI "?limit_bids=" + to_string(limit_bids) + "&limit_asks=" + to_string(limit_asks) + "&group=" + to_string(group); - DoGETrequest("/book/" + symbol, params); + doGETrequest("/book/" + symbol, params); } return *this; @@ -303,7 +314,7 @@ namespace BfxAPI string params = "?timestamp=" + to_string(since) + "&limit_trades=" + to_string(limit_trades); - DoGETrequest("/trades/" + symbol, params); + doGETrequest("/trades/" + symbol, params); } return *this; @@ -320,7 +331,7 @@ namespace BfxAPI string params = "?timestamp=" + to_string(since) + "&limit_lends=" + to_string(limit_lends); - DoGETrequest("/lends/" + currency, params); + doGETrequest("/lends/" + currency, params); } return *this; @@ -328,14 +339,14 @@ namespace BfxAPI BitfinexAPI& getSymbols() { - DoGETrequest("/symbols/", ""); + doGETrequest("/symbols/", ""); return *this; }; BitfinexAPI& getSymbolsDetails() { - DoGETrequest("/symbols_details/", ""); + doGETrequest("/symbols_details/", ""); return *this; }; @@ -350,7 +361,7 @@ namespace BfxAPI string params = "{\"request\":\"/v1/account_infos\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - DoPOSTrequest("/account_infos/", params); + doPOSTrequest("/account_infos/", params); return *this; }; @@ -360,7 +371,7 @@ namespace BfxAPI string params = "{\"request\":\"/v1/account_fees\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - DoPOSTrequest("/account_fees/", params); + doPOSTrequest("/account_fees/", params); return *this; }; @@ -370,7 +381,7 @@ namespace BfxAPI string params = "{\"request\":\"/v1/summary\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - DoPOSTrequest("/summary/", params); + doPOSTrequest("/summary/", params); return *this; }; @@ -391,7 +402,7 @@ namespace BfxAPI params += ",\"wallet_name\":\"" + walletName + "\""; params += ",\"renew\":" + to_string(renew); params += "}"; - DoPOSTrequest("/deposit/new/", params); + doPOSTrequest("/deposit/new/", params); return *this; }; @@ -401,7 +412,7 @@ namespace BfxAPI string params = "{\"request\":\"/v1/key_info\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - DoPOSTrequest("/key_info/", params); + doPOSTrequest("/key_info/", params); return *this; }; @@ -411,7 +422,7 @@ namespace BfxAPI string params = "{\"request\":\"/v1/margin_infos\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - DoPOSTrequest("/margin_infos/", params); + doPOSTrequest("/margin_infos/", params); return *this; }; @@ -421,7 +432,7 @@ namespace BfxAPI string params = "{\"request\":\"/v1/balances\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - DoPOSTrequest("/balances/", params); + doPOSTrequest("/balances/", params); return *this; }; @@ -445,7 +456,7 @@ namespace BfxAPI params += ",\"walletfrom\":\"" + walletfrom + "\""; params += ",\"walletto\":\"" + walletto + "\""; params += "}"; - DoPOSTrequest("/transfer/", params); + doPOSTrequest("/transfer/", params); return *this; }; @@ -463,7 +474,7 @@ namespace BfxAPI else { params += "}"; - DoPOSTrequest("/withdraw/", params); + doPOSTrequest("/withdraw/", params); } return *this; @@ -501,7 +512,7 @@ namespace BfxAPI params += ",\"buy_price_oco\":" + bool2string(buy_price_oco); params += "}"; - DoPOSTrequest("/order/new/", params); + doPOSTrequest("/order/new/", params); return *this; }; @@ -526,7 +537,7 @@ namespace BfxAPI params += ","; } params += "]}"; - DoPOSTrequest("/order/new/multi/", params); + doPOSTrequest("/order/new/multi/", params); return *this; }; @@ -537,7 +548,7 @@ namespace BfxAPI getTonce() + "\""; params += ",\"order_id\":" + to_string(order_id); params += "}"; - DoPOSTrequest("/order/cancel/", params); + doPOSTrequest("/order/cancel/", params); return *this; }; @@ -559,7 +570,7 @@ namespace BfxAPI params += ","; } params += "]}"; - DoPOSTrequest("/order/cancel/multi/", params); + doPOSTrequest("/order/cancel/multi/", params); return *this; }; @@ -569,7 +580,7 @@ namespace BfxAPI string params = "{\"request\":\"/v1/order/cancel/all\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - DoPOSTrequest("/order/cancel/all/", params); + doPOSTrequest("/order/cancel/all/", params); return *this; }; @@ -600,7 +611,7 @@ namespace BfxAPI params += ",\"is_hidden\":" + bool2string(is_hidden); params += ",\"use_all_available\":" + bool2string(use_remaining); params += "}"; - DoPOSTrequest("/order/cancel/replace/", params); + doPOSTrequest("/order/cancel/replace/", params); return *this; }; @@ -611,7 +622,7 @@ namespace BfxAPI getTonce() + "\""; params += ",\"order_id\":" + to_string(order_id); params += "}"; - DoPOSTrequest("/order/status/", params); + doPOSTrequest("/order/status/", params); return *this; }; @@ -621,7 +632,7 @@ namespace BfxAPI string params = "{\"request\":\"/v1/orders\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - DoPOSTrequest("/orders/", params); + doPOSTrequest("/orders/", params); return *this; }; @@ -632,7 +643,7 @@ namespace BfxAPI getTonce() + "\""; params += ",\"limit\":" + to_string(limit); params += "}"; - DoPOSTrequest("/orders/hist/", params); + doPOSTrequest("/orders/hist/", params); return *this; }; @@ -644,7 +655,7 @@ namespace BfxAPI string params = "{\"request\":\"/v1/positions\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - DoPOSTrequest("/positions/", params); + doPOSTrequest("/positions/", params); return *this; }; @@ -657,7 +668,7 @@ namespace BfxAPI params += ",\"position_id\":" + to_string(position_id); params += ",\"amount\":\"" + to_string(amount) + "\""; params += "}"; - DoPOSTrequest("/position/claim/", params); + doPOSTrequest("/position/claim/", params); return *this; }; @@ -691,7 +702,7 @@ namespace BfxAPI if (walletType != "all") params += ",\"wallet\":\"" + walletType + "\""; params += "}"; - DoPOSTrequest("/history/", params); + doPOSTrequest("/history/", params); return *this; }; @@ -718,7 +729,7 @@ namespace BfxAPI (!until ? getTonce() : to_string(until)) + "\""; params += ",\"limit\":" + to_string(limit); params += "}"; - DoPOSTrequest("/history/movements/", params); + doPOSTrequest("/history/movements/", params); return *this; }; @@ -742,7 +753,7 @@ namespace BfxAPI params += ",\"limit_trades\":" + to_string(limit_trades); params += ",\"reverse\":" + to_string(reverse); params += "}"; - DoPOSTrequest("/mytrades/", params); + doPOSTrequest("/mytrades/", params); } return *this; @@ -767,7 +778,7 @@ namespace BfxAPI params += ",\"period\":" + to_string(period); params += ",\"direction\":\"" + direction + "\""; params += "}"; - DoPOSTrequest("/offer/new/", params); + doPOSTrequest("/offer/new/", params); } return *this; @@ -779,7 +790,7 @@ namespace BfxAPI getTonce() + "\""; params += ",\"offer_id\":" + to_string(offer_id); params += "}"; - DoPOSTrequest("/offer/cancel/", params); + doPOSTrequest("/offer/cancel/", params); return *this; }; @@ -790,7 +801,7 @@ namespace BfxAPI getTonce() + "\""; params += ",\"offer_id\":" + to_string(offer_id); params += "}"; - DoPOSTrequest("/offer/status/", params); + doPOSTrequest("/offer/status/", params); return *this; }; @@ -800,7 +811,7 @@ namespace BfxAPI string params = "{\"request\":\"/v1/credits\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - DoPOSTrequest("/credits/", params); + doPOSTrequest("/credits/", params); return *this; }; @@ -810,7 +821,7 @@ namespace BfxAPI string params = "{\"request\":\"/v1/offers\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - DoPOSTrequest("/offers/", params); + doPOSTrequest("/offers/", params); return *this; }; @@ -821,7 +832,7 @@ namespace BfxAPI getTonce() + "\""; params += ",\"limit\":" + to_string(limit); params += "}"; - DoPOSTrequest("/offers/hist/", params); + doPOSTrequest("/offers/hist/", params); return *this; }; @@ -847,7 +858,7 @@ namespace BfxAPI params += ",\"until\":" + to_string(until); params += ",\"limit_trades\":" + to_string(limit_trades); params += "}"; - DoPOSTrequest("/mytrades_funding/", params); + doPOSTrequest("/mytrades_funding/", params); } return *this; @@ -858,7 +869,7 @@ namespace BfxAPI string params = "{\"request\":\"/v1/taken_funds\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - DoPOSTrequest("/taken_funds/", params); + doPOSTrequest("/taken_funds/", params); return *this; }; @@ -868,7 +879,7 @@ namespace BfxAPI string params = "{\"request\":\"/v1/unused_taken_funds\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - DoPOSTrequest("/unused_taken_funds/", params); + doPOSTrequest("/unused_taken_funds/", params); return *this; }; @@ -878,7 +889,7 @@ namespace BfxAPI string params = "{\"request\":\"/v1/total_taken_funds\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - DoPOSTrequest("/total_taken_funds/", params); + doPOSTrequest("/total_taken_funds/", params); return *this; }; @@ -889,7 +900,7 @@ namespace BfxAPI getTonce() + "\""; params += ",\"swap_id\":" + to_string(offer_id); params += "}"; - DoPOSTrequest("/funding/close/", params); + doPOSTrequest("/funding/close/", params); return *this; }; @@ -900,7 +911,7 @@ namespace BfxAPI getTonce() + "\""; params += ",\"position_id\":" + to_string(position_id); params += "}"; - DoPOSTrequest("/position/close/", params); + doPOSTrequest("/position/close/", params); return *this; }; @@ -1000,7 +1011,7 @@ namespace BfxAPI return noError; }; - void DoGETrequest(const string &UrlEndPoint, const string ¶ms) + void doGETrequest(const string &UrlEndPoint, const string ¶ms) { bfxApiStatusCode_ = noError; @@ -1009,8 +1020,9 @@ namespace BfxAPI string url = APIurl_ + UrlEndPoint + params; result_.clear(); - curl_easy_setopt(curlGET_, CURLOPT_TIMEOUT, 30L); + curl_easy_setopt(curlGET_, CURLOPT_TIMEOUT, CURL_TIMEOUT); curl_easy_setopt(curlGET_, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curlGET_, CURLOPT_VERBOSE, CURL_DEBUG_VERBOSE); curl_easy_setopt(curlGET_, CURLOPT_WRITEDATA, &result_); curl_easy_setopt(curlGET_, CURLOPT_WRITEFUNCTION, WriteCallback); @@ -1019,15 +1031,15 @@ namespace BfxAPI // libcurl internal error handling if (curlStatusCode_ != CURLE_OK) { - cout << "Libcurl error in DoGETrequest():\n"; - cout << "CURLcode: " << curlStatusCode_ << "\n"; + cerr << "libcurl error in doGETrequest():\n"; + cerr << "CURLcode: " << curlStatusCode_ << "\n"; } } else - cout << "curl not properly initialized curlGET_ = nullptr"; + cerr << "curl not properly initialized curlGET_ = nullptr"; }; - void DoPOSTrequest(const string &UrlEndPoint, const string ¶ms) + void doPOSTrequest(const string &UrlEndPoint, const string ¶ms) { bfxApiStatusCode_ = noError; @@ -1055,9 +1067,9 @@ namespace BfxAPI curl_easy_setopt(curlPOST_, CURLOPT_HTTPHEADER, httpHeaders); curl_easy_setopt(curlPOST_, CURLOPT_POST, 1); curl_easy_setopt(curlPOST_, CURLOPT_POSTFIELDS, "\n"); - curl_easy_setopt(curlPOST_, CURLOPT_TIMEOUT, 30L); + curl_easy_setopt(curlPOST_, CURLOPT_TIMEOUT, CURL_TIMEOUT); curl_easy_setopt(curlPOST_, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curlPOST_, CURLOPT_VERBOSE, 0L); // debug option + curl_easy_setopt(curlPOST_, CURLOPT_VERBOSE, CURL_DEBUG_VERBOSE); curl_easy_setopt(curlPOST_, CURLOPT_WRITEDATA, &result_); curl_easy_setopt(curlPOST_, CURLOPT_WRITEFUNCTION, WriteCallback); @@ -1066,13 +1078,13 @@ namespace BfxAPI // libcurl internal error handling if (curlStatusCode_ != CURLE_OK) { - cout << "Libcurl error in DoPOSTrequest():\n"; - cout << "CURLcode: " << curlStatusCode_ << "\n"; + cerr << "Libcurl error in doPOSTrequest():\n"; + cerr << "CURLcode: " << curlStatusCode_ << "\n"; } } else - cout << "curl not properly initialized curlPOST_ = nullptr"; + cerr << "curl not properly initialized curlPOST_ = nullptr"; }; //////////////////////////////////////////////////////////////////////// @@ -1101,9 +1113,7 @@ namespace BfxAPI byte buffer[1024] = {}; for (int i = 0; i < content.length(); ++i) - { buffer[i] = content[i]; - }; StringSource ss(buffer, content.length(), diff --git a/include/jsonutils.hpp b/include/jsonutils.hpp index d40b6a2..eecf585 100644 --- a/include/jsonutils.hpp +++ b/include/jsonutils.hpp @@ -49,6 +49,7 @@ namespace jsonutils fclose(pFileIn); remoteSchemaDoc = new rj::SchemaDocument(d); }; + ~MyRemoteSchemaDocumentProvider() { delete remoteSchemaDoc; From 1854b5668c7d8fca00ee5e09f300f69d8636e36a Mon Sep 17 00:00:00 2001 From: Maple Date: Mon, 2 Jul 2018 10:52:12 +0200 Subject: [PATCH 13/43] directory reorganize --- README.md | 4 ++-- include/{ => bfx-api-cpp}/BitfinexAPI.hpp | 0 include/{ => bfx-api-cpp}/jsonutils.hpp | 0 example.cpp => src/example.cpp | 4 ++-- 4 files changed, 4 insertions(+), 4 deletions(-) rename include/{ => bfx-api-cpp}/BitfinexAPI.hpp (100%) rename include/{ => bfx-api-cpp}/jsonutils.hpp (100%) rename example.cpp => src/example.cpp (98%) diff --git a/README.md b/README.md index 9d640f4..8e58709 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ This header-only library contains class for interfacing Bitfinex REST API v1. ### Installation Just copy content of `include/` directory into your project's `include/` directory and -add `#include "BitfinexAPI.hpp"` to your `.cpp` file. +add `#include "bfx-api-cpp/BitfinexAPI.hpp"` in your `.cpp` file. ### Usage @@ -35,7 +35,7 @@ add `#include "BitfinexAPI.hpp"` to your `.cpp` file. cout << bfxAPI.getCurlStatusCode() << endl; } -See self-explanatory `example.cpp` for general usage and more requests. +See self-explanatory `src/example.cpp` for general usage and more requests. ### Dependencies diff --git a/include/BitfinexAPI.hpp b/include/bfx-api-cpp/BitfinexAPI.hpp similarity index 100% rename from include/BitfinexAPI.hpp rename to include/bfx-api-cpp/BitfinexAPI.hpp diff --git a/include/jsonutils.hpp b/include/bfx-api-cpp/jsonutils.hpp similarity index 100% rename from include/jsonutils.hpp rename to include/bfx-api-cpp/jsonutils.hpp diff --git a/example.cpp b/src/example.cpp similarity index 98% rename from example.cpp rename to src/example.cpp index ebc38c5..4d9e389 100644 --- a/example.cpp +++ b/src/example.cpp @@ -12,7 +12,7 @@ #include // BitfinexAPI -#include "BitfinexAPI.hpp" +#include "bfx-api-cpp/BitfinexAPI.hpp" // namespaces @@ -31,7 +31,7 @@ int main(int argc, char *argv[]) // BfxAPI::BitfinexAPI bfxAPI("accessKey", "secretKey"); // Load API keys from file - ifstream ifs("key-secret", ifstream::in); + ifstream ifs("doc/key-secret", ifstream::in); if (!ifs.is_open()) { cerr << "Can't open 'key-secret' file. " << endl; From 10b612e111a0cfb9150aa1832eb0bad97c20b70f Mon Sep 17 00:00:00 2001 From: Maple Date: Tue, 3 Jul 2018 14:49:17 +0200 Subject: [PATCH 14/43] error header extracted --- doc/definitions.json | 2 +- include/bfx-api-cpp/BitfinexAPI.hpp | 22 ++-------------------- include/bfx-api-cpp/error.hpp | 24 ++++++++++++++++++++++++ include/bfx-api-cpp/jsonutils.hpp | 20 ++++++++++++++------ src/example.cpp | 6 ++++-- 5 files changed, 45 insertions(+), 29 deletions(-) create mode 100644 include/bfx-api-cpp/error.hpp diff --git a/doc/definitions.json b/doc/definitions.json index d7ad40f..e79662e 100644 --- a/doc/definitions.json +++ b/doc/definitions.json @@ -6,6 +6,6 @@ "items": { "type": "string" }, - "uniqueItems": true + "uniqueItems": false } } diff --git a/include/bfx-api-cpp/BitfinexAPI.hpp b/include/bfx-api-cpp/BitfinexAPI.hpp index 18fa967..74cae1b 100644 --- a/include/bfx-api-cpp/BitfinexAPI.hpp +++ b/include/bfx-api-cpp/BitfinexAPI.hpp @@ -47,6 +47,8 @@ // internal jsonutils #include "jsonutils.hpp" +// internal error +#include "error.hpp" // namespaces using std::cerr; @@ -80,26 +82,6 @@ namespace BfxAPI { public: - //////////////////////////////////////////////////////////////////////// - // Enumerations - //////////////////////////////////////////////////////////////////////// - - enum bfxERR - { - noError = 0, - curlERR, // 1 - badSymbol, // 2 - badCurrency, // 3 - badDepositMethod, // 4 - badWalletType, // 5 - requiredParamsMissing, // 6 - wireParamsMissing, // 7 - addressParamsMissing, // 8 - badOrderType, // 9 - jsonStrToUSetError, // 10 - badWDconfFilePath // 11 - }; - //////////////////////////////////////////////////////////////////////// // Typedefs //////////////////////////////////////////////////////////////////////// diff --git a/include/bfx-api-cpp/error.hpp b/include/bfx-api-cpp/error.hpp new file mode 100644 index 0000000..bae378b --- /dev/null +++ b/include/bfx-api-cpp/error.hpp @@ -0,0 +1,24 @@ +//////////////////////////////////////////////////////////////////////////////// +// error.hpp +// +// bfx-api-cpp error enumeration +// +//////////////////////////////////////////////////////////////////////////////// + +#pragma once + +enum bfxERR +{ + noError = 0, + curlERR, // 1 + badSymbol, // 2 + badCurrency, // 3 + badDepositMethod, // 4 + badWalletType, // 5 + requiredParamsMissing, // 6 + wireParamsMissing, // 7 + addressParamsMissing, // 8 + badOrderType, // 9 + jsonStrToUSetError, // 10 + badWDconfFilePath // 11 +}; diff --git a/include/bfx-api-cpp/jsonutils.hpp b/include/bfx-api-cpp/jsonutils.hpp index eecf585..c080009 100644 --- a/include/bfx-api-cpp/jsonutils.hpp +++ b/include/bfx-api-cpp/jsonutils.hpp @@ -14,6 +14,9 @@ #include "rapidjson/stringbuffer.h" #include "rapidjson/writer.h" +// internal error +#include "error.hpp" + // std #include #include @@ -115,7 +118,7 @@ namespace jsonutils } state_; }; - unsigned jsonStrToUset(unordered_set &uSet, string &jsonStr) + bfxERR jsonStrToUset(unordered_set &uSet, string &jsonStr) { // Create schema $ref resolver rj::Document sd; @@ -134,8 +137,13 @@ namespace jsonutils // Create reader rj::Reader reader; + /// DEBUG +// string mockJson = "{\"mid\":\"6581.55\",\"bid\":\"6581.5\",\"ask\":\"6581.6\",\"last_price\":\"6581.5\",\"low\":\"6333.2\",\"high\":\"6681.2\",\"volume\":\"28766.900098530004\",\"timestamp\":\"1530620498.4127066\"}"; + /// DEBUG + // Create input JSON StringStream rj::StringStream ss(jsonStr.c_str()); +// rj::StringStream ss(mockJson.c_str()); // Parse and validate if (!reader.Parse(ss, validator)) @@ -149,18 +157,18 @@ namespace jsonutils { rj::StringBuffer sb; validator.GetInvalidSchemaPointer().StringifyUriFragment(sb); - cout << "Invalid schema: " << sb.GetString() << endl; - cout << "Invalid keyword: " << validator.GetInvalidSchemaKeyword() << endl; + cerr << "Invalid schema: " << sb.GetString() << endl; + cerr << "Invalid keyword: " << validator.GetInvalidSchemaKeyword() << endl; sb.Clear(); validator.GetInvalidDocumentPointer().StringifyUriFragment(sb); - cout << "Invalid document: " << sb.GetString() << endl; + cerr << "Invalid document: " << sb.GetString() << endl; } - return 10; + return bfxERR::jsonStrToUSetError; } else { uSet.swap(handler.handlerUSet_); - return 0; + return bfxERR::noError; } } } diff --git a/src/example.cpp b/src/example.cpp index 4d9e389..801172d 100644 --- a/src/example.cpp +++ b/src/example.cpp @@ -49,9 +49,11 @@ int main(int argc, char *argv[]) else { // see bfxERR enum in BitfinexAPI.hpp::BitfinexAPI - cout << bfxAPI.getBfxApiStatusCode() << endl; + cerr << "BfxApiStatusCode: "; + cerr << bfxAPI.getBfxApiStatusCode() << endl; // see https://curl.haxx.se/libcurl/c/libcurl-errors.html - cout << bfxAPI.getCurlStatusCode() << endl; + cerr << "CurlStatusCode: "; + cerr << bfxAPI.getCurlStatusCode() << endl; } cout << "Request without error checking: " << endl; From 97396599d71800251782b9774351905349620b2c Mon Sep 17 00:00:00 2001 From: Maple Date: Tue, 3 Jul 2018 17:16:32 +0200 Subject: [PATCH 15/43] definitions.json edit --- doc/definitions.json | 57 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/doc/definitions.json b/doc/definitions.json index e79662e..147a97b 100644 --- a/doc/definitions.json +++ b/doc/definitions.json @@ -1,11 +1,50 @@ { - "flatJsonSchema": { - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Flat JSON string array scheme used in jsonutils::jsonStrToUset", - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": false + "$schema": "http://json-schema.org/draft-04/schema#", + "flatJsonSchema": { + "description": "Flat JSON string array scheme used in jsonutils::jsonStrToUset", + "type": "array", + "items": { + "type": "string" } -} + }, + "pubticker": { + "description": "JSON schema used in https://api.bitfinex.com/v1/pubticker/ endpoint", + "type": "object", + "properties": { + "mid": { + "type": "string" + }, + "bid": { + "type": "string" + }, + "ask": { + "type": "string" + }, + "last_price": { + "type": "string" + }, + "low": { + "type": "string" + }, + "high": { + "type": "string" + }, + "volume": { + "type": "string" + }, + "timestamp": { + "type": "string" + } + }, + "required": [ + "mid", + "bid", + "ask", + "last_price", + "low", + "high", + "volume", + "timestamp" + ] + } +} \ No newline at end of file From dad8fabcac6ee1440ad2ce355b8b8a3b23d2e151 Mon Sep 17 00:00:00 2001 From: Maple Date: Tue, 3 Jul 2018 17:41:51 +0200 Subject: [PATCH 16/43] last working --- include/bfx-api-cpp/BitfinexAPI.hpp | 25 ++++++++++++------------- include/bfx-api-cpp/jsonutils.hpp | 4 ++-- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/include/bfx-api-cpp/BitfinexAPI.hpp b/include/bfx-api-cpp/BitfinexAPI.hpp index 74cae1b..3410179 100644 --- a/include/bfx-api-cpp/BitfinexAPI.hpp +++ b/include/bfx-api-cpp/BitfinexAPI.hpp @@ -115,8 +115,7 @@ namespace BfxAPI bfxApiStatusCode_(noError) { // populate _symbols directly from Bitfinex getSymbols endpoint - getSymbols(); - jsonutils::jsonStrToUset(symbols_, result_); + jsonutils::jsonStrToUset(symbols_, getSymbols().strResponse()); result_.clear(); currencies_ = @@ -904,17 +903,21 @@ namespace BfxAPI // Private attributes //////////////////////////////////////////////////////////////////////// + // containers with supported parameters unordered_set symbols_; // valid symbol pairs unordered_set currencies_; // valid currencies unordered_set methods_; // valid deposit methods unordered_set walletNames_; // valid walletTypes unordered_set types_; // valid Types (see new order endpoint) - string WDconfFilePath_; - string APIurl_; - string accessKey_, secretKey_; + // CURL instances CURL *curlGET_; CURL *curlPOST_; CURLcode curlStatusCode_; + // BitfinexAPI settings + string WDconfFilePath_; + string APIurl_; + string accessKey_, secretKey_; + // dynamic and status variables bfxERR bfxApiStatusCode_; string result_; @@ -1086,7 +1089,7 @@ namespace BfxAPI return to_string(ms.count()); }; - static int getBase64(const string &content, string &encoded) + static void getBase64(const string &content, string &encoded) { using CryptoPP::Base64Encoder; using CryptoPP::StringSink; @@ -1101,13 +1104,11 @@ namespace BfxAPI content.length(), true, new Base64Encoder(new StringSink(encoded), false)); - - return 0; }; - static int getHmacSha384(const string &key, - const string &content, - string &digest) + static void getHmacSha384(const string &key, + const string &content, + string &digest) { using CryptoPP::HashFilter; using CryptoPP::HexEncoder; @@ -1127,8 +1128,6 @@ namespace BfxAPI new HashFilter(hmac, new StringSink(mac))); StringSource ss2(mac, true, new HexEncoder(new StringSink(digest))); transform(digest.cbegin(), digest.cend(), digest.begin(), ::tolower); - - return 0; }; // Curl write callback function. Appends fetched *content to *userp diff --git a/include/bfx-api-cpp/jsonutils.hpp b/include/bfx-api-cpp/jsonutils.hpp index c080009..62efaa3 100644 --- a/include/bfx-api-cpp/jsonutils.hpp +++ b/include/bfx-api-cpp/jsonutils.hpp @@ -118,7 +118,7 @@ namespace jsonutils } state_; }; - bfxERR jsonStrToUset(unordered_set &uSet, string &jsonStr) + bfxERR jsonStrToUset(unordered_set &uSet, const string &jsonStr) { // Create schema $ref resolver rj::Document sd; @@ -143,7 +143,7 @@ namespace jsonutils // Create input JSON StringStream rj::StringStream ss(jsonStr.c_str()); -// rj::StringStream ss(mockJson.c_str()); +// rj::StringStream ss(mockJson.c_str()); // DEBUG // Parse and validate if (!reader.Parse(ss, validator)) From da82a41ac4586cd4aecfeb399f402d4a3c7f9d82 Mon Sep 17 00:00:00 2001 From: Maple Date: Wed, 4 Jul 2018 08:14:33 +0200 Subject: [PATCH 17/43] readme update --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 9d640f4..1f5538e 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ _C++ Bitfinex REST API client_ *** +### Notice + +***Master*** branch contains new refactored version of the client. For old version checkout ***legacy*** branch. + ### Synopsis This header-only library contains class for interfacing Bitfinex REST API v1. From e96b848f66b317827e99588bf166d61cd9532cb3 Mon Sep 17 00:00:00 2001 From: Maple Date: Wed, 11 Jul 2018 10:39:10 +0200 Subject: [PATCH 18/43] schema validation logic complete --- doc/definitions.json | 8 ++ include/bfx-api-cpp/BitfinexAPI.hpp | 21 +++- include/bfx-api-cpp/error.hpp | 4 +- include/bfx-api-cpp/jsonutils.hpp | 143 ++++++++++++++++++++++++---- 4 files changed, 157 insertions(+), 19 deletions(-) diff --git a/doc/definitions.json b/doc/definitions.json index 147a97b..fcbc52f 100644 --- a/doc/definitions.json +++ b/doc/definitions.json @@ -7,6 +7,14 @@ "type": "string" } }, + "symbols": { + "description": "JSON schema used in https://api.bitfinex.com/v1/symbols/ endpoint", + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": false + }, "pubticker": { "description": "JSON schema used in https://api.bitfinex.com/v1/pubticker/ endpoint", "type": "object", diff --git a/include/bfx-api-cpp/BitfinexAPI.hpp b/include/bfx-api-cpp/BitfinexAPI.hpp index 3410179..b6e6f85 100644 --- a/include/bfx-api-cpp/BitfinexAPI.hpp +++ b/include/bfx-api-cpp/BitfinexAPI.hpp @@ -137,6 +137,9 @@ namespace BfxAPI "XRP", "ZEC" }; + + schemaValidator_ = jsonutils::BfxSchemaValidator(symbols_, currencies_); + // As found on // https://bitfinex.readme.io/v1/reference#rest-auth-deposit methods_ = @@ -152,10 +155,12 @@ namespace BfxAPI "tetheruso", "zcash", }; + walletNames_ = { "trading", "exchange", "deposit" }; + // New order endpoint "type" parameter types_ = { @@ -913,6 +918,8 @@ namespace BfxAPI CURL *curlGET_; CURL *curlPOST_; CURLcode curlStatusCode_; + // internal jsonutils instances + jsonutils::BfxSchemaValidator schemaValidator_; // BitfinexAPI settings string WDconfFilePath_; string APIurl_; @@ -996,13 +1003,13 @@ namespace BfxAPI return noError; }; - void doGETrequest(const string &UrlEndPoint, const string ¶ms) + void doGETrequest(const string &apiEndPoint, const string ¶ms) { bfxApiStatusCode_ = noError; if(curlGET_) { - string url = APIurl_ + UrlEndPoint + params; + string url = APIurl_ + apiEndPoint + params; result_.clear(); curl_easy_setopt(curlGET_, CURLOPT_TIMEOUT, CURL_TIMEOUT); @@ -1018,10 +1025,20 @@ namespace BfxAPI { cerr << "libcurl error in doGETrequest():\n"; cerr << "CURLcode: " << curlStatusCode_ << "\n"; + bfxApiStatusCode_ = curlERR; + } + // Check schema + else + { + bfxApiStatusCode_ = + schemaValidator_.validateSchema(apiEndPoint, result_); } } else + { cerr << "curl not properly initialized curlGET_ = nullptr"; + bfxApiStatusCode_ = curlERR; + } }; void doPOSTrequest(const string &UrlEndPoint, const string ¶ms) diff --git a/include/bfx-api-cpp/error.hpp b/include/bfx-api-cpp/error.hpp index bae378b..e8d7eb1 100644 --- a/include/bfx-api-cpp/error.hpp +++ b/include/bfx-api-cpp/error.hpp @@ -20,5 +20,7 @@ enum bfxERR addressParamsMissing, // 8 badOrderType, // 9 jsonStrToUSetError, // 10 - badWDconfFilePath // 11 + badWDconfFilePath, // 11 + responseParseError, // 12 + responseSchemaError // 13 }; diff --git a/include/bfx-api-cpp/jsonutils.hpp b/include/bfx-api-cpp/jsonutils.hpp index 62efaa3..e454b31 100644 --- a/include/bfx-api-cpp/jsonutils.hpp +++ b/include/bfx-api-cpp/jsonutils.hpp @@ -21,6 +21,7 @@ #include #include #include +#include // namespaces using std::cerr; @@ -28,16 +29,18 @@ using std::cout; using std::endl; using std::string; using std::unordered_set; +using std::unordered_map; namespace rj = rapidjson; -/////////////////////////////////////////////////////////////////////////////// -// Routines -/////////////////////////////////////////////////////////////////////////////// - namespace jsonutils { - // Helper class resolving remote schema for schema $ref operator + + //////////////////////////////////////////////////////////////////////////// + // Classes + //////////////////////////////////////////////////////////////////////////// + + /// Helper class resolving remote schema for schema $ref operator class MyRemoteSchemaDocumentProvider: public rj::IRemoteSchemaDocumentProvider { public: @@ -50,27 +53,129 @@ namespace jsonutils rj::Document d; d.ParseStream(fReadStream); fclose(pFileIn); - remoteSchemaDoc = new rj::SchemaDocument(d); + remoteSchemaDoc_ = new rj::SchemaDocument(d); }; - ~MyRemoteSchemaDocumentProvider() - { - delete remoteSchemaDoc; - }; +// ~MyRemoteSchemaDocumentProvider() +// { +// delete remoteSchemaDoc_; +// }; private: - rj::SchemaDocument *remoteSchemaDoc; + rj::SchemaDocument *remoteSchemaDoc_; virtual const rj::SchemaDocument* GetRemoteDocument(const char* uri, rj::SizeType length) { // Resolve the URI and return a pointer to that schema - return remoteSchemaDoc; + return remoteSchemaDoc_; } }; - // SAX events helper struct for jsonStrToUset() routine + class BfxSchemaValidator + { + public: + + BfxSchemaValidator() {} + BfxSchemaValidator(unordered_set &symbols, + unordered_set ¤cies) + { + // Mapping is needed because rapidjson implementation of $ref + // keyword in json schema doesn't support json keys which contain + // special characters. + // See https://github.com/Tencent/rapidjson/issues/1311 + + // Create map for endpoints which use symbols in URL + for (const auto &symbol : symbols) + { + // Map /pubticker/[symbol] endpoints to "pubticker" schema + apiEndPointToSchemaMap_.emplace("/pubticker/" + symbol, + "pubticker"); + // Map /stats/[symbol] endpoints to "stats" schema + apiEndPointToSchemaMap_.emplace("/stats/" + symbol, + "stats"); + // Map /book/[symbol] endpoints to "book" schema + apiEndPointToSchemaMap_.emplace("/book/" + symbol, + "book"); + // Map /trades/[symbol] endpoints to "trades" schema + apiEndPointToSchemaMap_.emplace("/trades/" + symbol, + "trades"); + } + + // Create map for endpoints which use currencies in URL + for (const auto ¤cy : currencies) + { + // Map /lendbook/[currency] endpoints to "lendbook" schema + apiEndPointToSchemaMap_.emplace("/lendbook/" + currency, + "lendbook"); + // Map /lends/[currency] endpoints to "lends" schema + apiEndPointToSchemaMap_.emplace("/lends/" + currency, + "lends"); + } + + // Create map for static endpoints + apiEndPointToSchemaMap_.emplace("/symbols/", "symbols"); + apiEndPointToSchemaMap_.emplace("/symbols_details/", + "symbols_details"); + + } + + auto validateSchema(const string &apiEndPoint, const string &inputJson) + { + const auto schemaName = getApiEndPointSchemaName(apiEndPoint); + + // Create rapidjson schema document + rj::Document sd; + string schema = + "{ \"$ref\": \"definitions.json#/" + schemaName + "\" }"; + sd.Parse(schema.c_str()); + rj::SchemaDocument schemaDocument(sd, 0, 0, &provider_); + + // Create rapidjson document and check for parse errors + rj::Document d; + if (d.Parse(inputJson.c_str()).HasParseError()) + { + cerr << "Invalid json - response:" << endl; + cerr << inputJson << endl; + cerr << "API endpoint: " << apiEndPoint << endl; + return bfxERR::responseParseError; + } + + // Create rapidjson validator and check for schema errors + rj::SchemaValidator validator(schemaDocument); + if (!d.Accept(validator)) + { + // Input JSON is invalid according to the schema + // Output diagnostic information + rj::StringBuffer sb; + validator.GetInvalidSchemaPointer().StringifyUriFragment(sb); + cerr << "Invalid schema: " << sb.GetString() << endl; + cerr << "Invalid keyword: " << validator.GetInvalidSchemaKeyword() << endl; + sb.Clear(); + validator.GetInvalidDocumentPointer().StringifyUriFragment(sb); + cerr << "Invalid document: " << sb.GetString() << endl; + cerr << "Invalid response: " << inputJson << endl; + cerr << "Invalid API endpoint: " << apiEndPoint << endl; + return bfxERR::responseSchemaError; + } + + return bfxERR::noError; + } + + private: + + MyRemoteSchemaDocumentProvider provider_; + unordered_map apiEndPointToSchemaMap_; + + const string& getApiEndPointSchemaName(const string& apiEndpoint) noexcept + { + return apiEndPointToSchemaMap_[apiEndpoint]; + } + + }; + + /// SAX events helper struct for jsonStrToUset() routine struct jsonStrToUsetHandler: public rj::BaseReaderHandler, jsonStrToUsetHandler> { @@ -118,7 +223,11 @@ namespace jsonutils } state_; }; - bfxERR jsonStrToUset(unordered_set &uSet, const string &jsonStr) + //////////////////////////////////////////////////////////////////////////// + // Routines + //////////////////////////////////////////////////////////////////////////// + + bfxERR jsonStrToUset(unordered_set &uSet, const string &inputJson) { // Create schema $ref resolver rj::Document sd; @@ -132,7 +241,8 @@ namespace jsonutils jsonStrToUsetHandler handler; // Create schema validator - rj::GenericSchemaValidator validator(schemaDoc, handler); + rj::GenericSchemaValidator + validator(schemaDoc, handler); // Create reader rj::Reader reader; @@ -142,7 +252,7 @@ namespace jsonutils /// DEBUG // Create input JSON StringStream - rj::StringStream ss(jsonStr.c_str()); + rj::StringStream ss(inputJson.c_str()); // rj::StringStream ss(mockJson.c_str()); // DEBUG // Parse and validate @@ -162,6 +272,7 @@ namespace jsonutils sb.Clear(); validator.GetInvalidDocumentPointer().StringifyUriFragment(sb); cerr << "Invalid document: " << sb.GetString() << endl; + cerr << "Invalid response: " << inputJson << endl; } return bfxERR::jsonStrToUSetError; } From 702eee88dc57bafdea4dbeb841f446596657b632 Mon Sep 17 00:00:00 2001 From: Maple Date: Wed, 11 Jul 2018 14:01:02 +0200 Subject: [PATCH 19/43] schema validatation --- README.md | 16 +- doc/definitions.json | 264 +++++++++++++++++++++++++++- include/bfx-api-cpp/BitfinexAPI.hpp | 17 +- include/bfx-api-cpp/jsonutils.hpp | 62 ++++++- src/example.cpp | 30 +++- 5 files changed, 358 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 426d91f..cf17452 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,20 @@ _C++ Bitfinex REST API client_ ### Notice -***Master*** branch contains new refactored version of the client. For old version checkout ***legacy*** branch. +***Master*** branch contains new version of the client. For old version checkout ***legacy*** branch. ### Synopsis -This header-only library contains class for interfacing Bitfinex REST API v1. +This header-only library contains class for interfacing Bitfinex REST API v1. Current version supports response JSON +schema validation. ### Installation -Just copy content of `include/` directory into your project's `include/` directory and +1. Copy content of `include/` directory into your project's `include/` directory and add `#include "bfx-api-cpp/BitfinexAPI.hpp"` in your `.cpp` file. +2. Copy `doc/` directory with configuration files into your project. + ### Usage // Create API client for both authenticated and unauthenticated requests @@ -50,9 +53,12 @@ See self-explanatory `src/example.cpp` for general usage and more requests. ### ToDo -- [ ] Integrating RapidJSON - [ ] Unit tests -- [ ] JSON Scheme validation +- [x] JSON Scheme validation + +### Change Log + +- 2018-07-11 Schema validation logic complete. Client currently validates public requests only. ### Author diff --git a/doc/definitions.json b/doc/definitions.json index fcbc52f..4f069e5 100644 --- a/doc/definitions.json +++ b/doc/definitions.json @@ -7,14 +7,6 @@ "type": "string" } }, - "symbols": { - "description": "JSON schema used in https://api.bitfinex.com/v1/symbols/ endpoint", - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": false - }, "pubticker": { "description": "JSON schema used in https://api.bitfinex.com/v1/pubticker/ endpoint", "type": "object", @@ -54,5 +46,261 @@ "volume", "timestamp" ] + }, + "stats": { + "description": "JSON schema used in https://api.bitfinex.com/v1/stats/ endpoint", + "type": "array", + "items": { + "type": "object", + "properties": { + "period": { + "type": "integer" + }, + "volume": { + "type": "string" + } + }, + "required": [ + "period", + "volume" + ] + } + }, + "lendbook": { + "description": "JSON schema used in https://api.bitfinex.com/v1/lendbook/ endpoint", + "type": "object", + "properties": { + "bids": { + "type": "array", + "items": { + "type": "object", + "properties": { + "rate": { + "type": "string" + }, + "amount": { + "type": "string" + }, + "period": { + "type": "integer" + }, + "timestamp": { + "type": "string" + }, + "frr": { + "type": "string" + } + }, + "required": [ + "rate", + "amount", + "period", + "timestamp", + "frr" + ] + } + }, + "asks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "rate": { + "type": "string" + }, + "amount": { + "type": "string" + }, + "period": { + "type": "integer" + }, + "timestamp": { + "type": "string" + }, + "frr": { + "type": "string" + } + }, + "required": [ + "rate", + "amount", + "period", + "timestamp", + "frr" + ] + } + } + }, + "required": [ + "bids", + "asks" + ] + }, + "book": { + "description": "JSON schema used in https://api.bitfinex.com/v1/book/ endpoint", + "type": "object", + "properties": { + "bids": { + "type": "array", + "items": { + "type": "object", + "properties": { + "price": { + "type": "string" + }, + "amount": { + "type": "string" + }, + "timestamp": { + "type": "string" + } + }, + "required": [ + "price", + "amount", + "timestamp" + ] + } + }, + "asks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "price": { + "type": "string" + }, + "amount": { + "type": "string" + }, + "timestamp": { + "type": "string" + } + }, + "required": [ + "price", + "amount", + "timestamp" + ] + } + } + }, + "required": [ + "bids", + "asks" + ] + }, + "trades": { + "description": "JSON schema used in https://api.bitfinex.com/v1/trades/ endpoint", + "type": "array", + "items": { + "type": "object", + "properties": { + "timestamp": { + "type": "integer" + }, + "tid": { + "type": "integer" + }, + "price": { + "type": "string" + }, + "amount": { + "type": "string" + }, + "exchange": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "timestamp", + "tid", + "price", + "amount", + "exchange", + "type" + ] + } + }, + "lends": { + "description": "JSON schema used in https://api.bitfinex.com/v1/lends/ endpoint", + "type": "array", + "items": { + "type": "object", + "properties": { + "rate": { + "type": "string" + }, + "amount_lent": { + "type": "string" + }, + "amount_used": { + "type": "string" + }, + "timestamp": { + "type": "integer" + } + }, + "required": [ + "rate", + "amount_lent", + "amount_used", + "timestamp" + ] + } + }, + "symbols": { + "description": "JSON schema used in https://api.bitfinex.com/v1/symbols/ endpoint", + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": false + }, + "symbols_details": { + "description": "JSON schema used in https://api.bitfinex.com/v1/symbols_details/ endpoint", + "type": "array", + "items": { + "type": "object", + "properties": { + "pair": { + "type": "string" + }, + "price_precision": { + "type": "integer" + }, + "initial_margin": { + "type": "string" + }, + "minimum_margin": { + "type": "string" + }, + "maximum_order_size": { + "type": "string" + }, + "minimum_order_size": { + "type": "string" + }, + "expiration": { + "type": "string" + }, + "margin": { + "type": "boolean" + } + }, + "required": [ + "pair", + "price_precision", + "initial_margin", + "minimum_margin", + "maximum_order_size", + "minimum_order_size", + "expiration", + "margin" + ] + } } } \ No newline at end of file diff --git a/include/bfx-api-cpp/BitfinexAPI.hpp b/include/bfx-api-cpp/BitfinexAPI.hpp index b6e6f85..19f3600 100644 --- a/include/bfx-api-cpp/BitfinexAPI.hpp +++ b/include/bfx-api-cpp/BitfinexAPI.hpp @@ -1041,13 +1041,13 @@ namespace BfxAPI } }; - void doPOSTrequest(const string &UrlEndPoint, const string ¶ms) + void doPOSTrequest(const string &apiEndPoint, const string ¶ms) { bfxApiStatusCode_ = noError; if(curlPOST_) { - string url = APIurl_ + UrlEndPoint; + string url = APIurl_ + apiEndPoint; string payload; string signature; getBase64(params, payload); @@ -1082,11 +1082,21 @@ namespace BfxAPI { cerr << "Libcurl error in doPOSTrequest():\n"; cerr << "CURLcode: " << curlStatusCode_ << "\n"; + bfxApiStatusCode_ = curlERR; + } + // Check schema + else + { + bfxApiStatusCode_ = + schemaValidator_.validateSchema(apiEndPoint, result_); } } else + { cerr << "curl not properly initialized curlPOST_ = nullptr"; + bfxApiStatusCode_ = curlERR; + } }; //////////////////////////////////////////////////////////////////////// @@ -1156,7 +1166,8 @@ namespace BfxAPI size_t nmemb, void *userp) noexcept { - (static_cast(userp))->append(static_cast(response)); + (static_cast(userp))-> + append(static_cast(response), size * nmemb); return size * nmemb; }; diff --git a/include/bfx-api-cpp/jsonutils.hpp b/include/bfx-api-cpp/jsonutils.hpp index e454b31..156bc5f 100644 --- a/include/bfx-api-cpp/jsonutils.hpp +++ b/include/bfx-api-cpp/jsonutils.hpp @@ -82,10 +82,15 @@ namespace jsonutils unordered_set ¤cies) { // Mapping is needed because rapidjson implementation of $ref - // keyword in json schema doesn't support json keys which contain - // special characters. + // keyword in json schema doesn't support json schema names which + // contain special characters thus direct mapping of endpoint such + // "/symbols/" to "/symbols/" schema name is not possible. // See https://github.com/Tencent/rapidjson/issues/1311 + //////////////////////////////////////////////////////////////////// + // Public endpoints + //////////////////////////////////////////////////////////////////// + // Create map for endpoints which use symbols in URL for (const auto &symbol : symbols) { @@ -119,6 +124,54 @@ namespace jsonutils apiEndPointToSchemaMap_.emplace("/symbols_details/", "symbols_details"); + //////////////////////////////////////////////////////////////////// + // Authenticated endpoints + //////////////////////////////////////////////////////////////////// + + // Create map for static endpoints + apiEndPointToSchemaMap_.emplace("/account_infos/", "account_infos"); + apiEndPointToSchemaMap_.emplace("/account_fees/", "account_fees"); + apiEndPointToSchemaMap_.emplace("/summary/", "summary"); + apiEndPointToSchemaMap_.emplace("/deposit/new/", "deposit_new"); + apiEndPointToSchemaMap_.emplace("/key_info/", "key_info"); + apiEndPointToSchemaMap_.emplace("/margin_infos/", "margin_infos"); + apiEndPointToSchemaMap_.emplace("/balances/", "balances"); + apiEndPointToSchemaMap_.emplace("/transfer/", "transfer"); + apiEndPointToSchemaMap_.emplace("/withdraw/", "withdraw"); + apiEndPointToSchemaMap_.emplace("/order/new/", "order_new"); + apiEndPointToSchemaMap_.emplace("/order/new/multi/", "order_new_multi"); + apiEndPointToSchemaMap_.emplace("/order/cancel/", "order_cancel"); + apiEndPointToSchemaMap_.emplace("/order/cancel/multi/", + "order_cancel_multi"); + apiEndPointToSchemaMap_.emplace("/order/cancel/all/", + "order_cancel_all"); + apiEndPointToSchemaMap_.emplace("/order/cancel/replace/", + "order_cancel_replace"); + apiEndPointToSchemaMap_.emplace("/order/status/", "order_status"); + apiEndPointToSchemaMap_.emplace("/orders/", "orders"); + apiEndPointToSchemaMap_.emplace("/orders/hist/", "orders_hist"); + apiEndPointToSchemaMap_.emplace("/positions/", "positions"); + apiEndPointToSchemaMap_.emplace("/position/claim/", "position_claim"); + apiEndPointToSchemaMap_.emplace("/history/", "history"); + apiEndPointToSchemaMap_.emplace("/history/movements/", + "history_movements"); + apiEndPointToSchemaMap_.emplace("/mytrades/", "mytrades"); + apiEndPointToSchemaMap_.emplace("/offer/new/", "offer_new"); + apiEndPointToSchemaMap_.emplace("/offer/cancel/", "offer_cancel"); + apiEndPointToSchemaMap_.emplace("/offer/status/", "offer_status"); + apiEndPointToSchemaMap_.emplace("/credits/", "credits"); + apiEndPointToSchemaMap_.emplace("/offers/", "offers"); + apiEndPointToSchemaMap_.emplace("/offers/hist/", "offers_hist"); + apiEndPointToSchemaMap_.emplace("/mytrades_funding/", + "mytrades_funding"); + apiEndPointToSchemaMap_.emplace("/taken_funds/", "taken_funds"); + apiEndPointToSchemaMap_.emplace("/unused_taken_funds/", + "unused_taken_funds"); + apiEndPointToSchemaMap_.emplace("/total_taken_funds/", + "total_taken_funds"); + apiEndPointToSchemaMap_.emplace("/funding/close/", "funding_close"); + apiEndPointToSchemaMap_.emplace("/position/close/", "position_close"); + } auto validateSchema(const string &apiEndPoint, const string &inputJson) @@ -247,13 +300,8 @@ namespace jsonutils // Create reader rj::Reader reader; - /// DEBUG -// string mockJson = "{\"mid\":\"6581.55\",\"bid\":\"6581.5\",\"ask\":\"6581.6\",\"last_price\":\"6581.5\",\"low\":\"6333.2\",\"high\":\"6681.2\",\"volume\":\"28766.900098530004\",\"timestamp\":\"1530620498.4127066\"}"; - /// DEBUG - // Create input JSON StringStream rj::StringStream ss(inputJson.c_str()); -// rj::StringStream ss(mockJson.c_str()); // DEBUG // Parse and validate if (!reader.Parse(ss, validator)) diff --git a/src/example.cpp b/src/example.cpp index 801172d..880ebca 100644 --- a/src/example.cpp +++ b/src/example.cpp @@ -66,11 +66,11 @@ int main(int argc, char *argv[]) // bfxAPI.getTicker("btcusd"); // bfxAPI.getStats("btcusd"); // bfxAPI.getFundingBook("USD", 50, 50); - // bfxAPI.getOrderBook("btcusd", 50, 50, 1); + // bfxAPI.getOrderBook("btcusd", 50, 50, true); // bfxAPI.getTrades("btcusd", 0L, 50); // bfxAPI.getLends("USD", 0L, 50); // bfxAPI.getSymbols(); - // bfxAPI.getSymbolDetails(); + // bfxAPI.getSymbolsDetails(); //////////////////////////////////////////////////////////////////////////// /// Available authenticated requests @@ -80,7 +80,7 @@ int main(int argc, char *argv[]) // bfxAPI.getAccountInfo(); // bfxAPI.getAccountFees(); // bfxAPI.getSummary(); - // bfxAPI.deposit("bitcoin", "deposit", 1); + // bfxAPI.deposit("bitcoin", "deposit", true); // bfxAPI.getKeyPermissions(); // bfxAPI.getMarginInfos(); // bfxAPI.getBalances(); @@ -88,8 +88,16 @@ int main(int argc, char *argv[]) // bfxAPI.withdraw(); // configure withdraw.conf file before use /// Orders /// - // bfxAPI.newOrder("btcusd", 0.01, 983, "sell", "exchange limit", 0, 1, - // 0, 0, 0); + // bfxAPI.newOrder("btcusd", + // 0.01, + // 983, + // "sell", + // "exchange limit", + // false, + // true, + // false, + // false, + // 0); // // How to create vOrders object for newOrders() call // BitfinexAPI::vOrders orders = @@ -112,8 +120,14 @@ int main(int argc, char *argv[]) // bfxAPI.cancelOrders(ids); // // bfxAPI.cancelAllOrders(); - // bfxAPI.replaceOrder(1321548521LL, "btcusd", 0.05, 1212, "sell", - // "exchange limit", 0, 0); + // bfxAPI.replaceOrder(1321548521LL, + // "btcusd", + // 0.05, + // 1212, + // "sell", + // "exchange limit", + // false, + // false); // bfxAPI.getOrderStatus(12113548453LL); // bfxAPI.getActiveOrders(); // bfxAPI.getOrdersHistory(10); @@ -125,7 +139,7 @@ int main(int argc, char *argv[]) /// Historical data /// // bfxAPI.getBalanceHistory("USD", 0L, 0L, 500, "all"); // bfxAPI.getWithdrawalHistory("BTC", "all", 0L , 0L, 500); - // bfxAPI.getPastTrades("btcusd", 0L, 0L, 500, 0); + // bfxAPI.getPastTrades("btcusd", 0L, 0L, 500, false); /// Margin funding /// // bfxAPI.newOffer("USD", 12000, 25.2, 30, "lend"); From bf1418cf1eb9a4cd1fcb36fe745b62efb3344440 Mon Sep 17 00:00:00 2001 From: Maple Date: Thu, 12 Jul 2018 10:21:13 +0200 Subject: [PATCH 20/43] named constants moved into class --- include/bfx-api-cpp/BitfinexAPI.hpp | 26 +++++++++++++------------- include/bfx-api-cpp/error.hpp | 2 +- include/bfx-api-cpp/jsonutils.hpp | 12 ++++++------ 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/include/bfx-api-cpp/BitfinexAPI.hpp b/include/bfx-api-cpp/BitfinexAPI.hpp index 19f3600..029776a 100644 --- a/include/bfx-api-cpp/BitfinexAPI.hpp +++ b/include/bfx-api-cpp/BitfinexAPI.hpp @@ -68,20 +68,20 @@ using CryptoPP::byte; namespace BfxAPI { - //////////////////////////////////////////////////////////////////////// - // Global variables - //////////////////////////////////////////////////////////////////////// - - const auto API_URL = "https://api.bitfinex.com/v1"; - const auto CURL_TIMEOUT = 30L; - const auto CURL_DEBUG_VERBOSE = 0L; - const auto WITHDRAWAL_CONF_FILE_PATH = "doc/withdraw.conf"; - class BitfinexAPI { public: + //////////////////////////////////////////////////////////////////////// + // Class constants + //////////////////////////////////////////////////////////////////////// + + static constexpr auto API_URL = "https://api.bitfinex.com/v1"; + static constexpr auto CURL_TIMEOUT = 30L; + static constexpr auto CURL_DEBUG_VERBOSE = 0L; + static constexpr auto WITHDRAWAL_CONF_FILE_PATH = "doc/withdraw.conf"; + //////////////////////////////////////////////////////////////////////// // Typedefs //////////////////////////////////////////////////////////////////////// @@ -199,7 +199,7 @@ namespace BfxAPI const string getWDconfFilePath() const noexcept { return WDconfFilePath_; } - const bfxERR& getBfxApiStatusCode() const noexcept + const BfxClientErrors& getBfxApiStatusCode() const noexcept { return bfxApiStatusCode_; } const CURLcode& getCurlStatusCode() const noexcept @@ -454,7 +454,7 @@ namespace BfxAPI getTonce() + "\""; // Add params from withdraw.conf - bfxERR code(parseWDconfParams(params)); + BfxClientErrors code(parseWDconfParams(params)); if (code != noError) bfxApiStatusCode_ = code; else @@ -925,14 +925,14 @@ namespace BfxAPI string APIurl_; string accessKey_, secretKey_; // dynamic and status variables - bfxERR bfxApiStatusCode_; + BfxClientErrors bfxApiStatusCode_; string result_; //////////////////////////////////////////////////////////////////////// // Utility private methods //////////////////////////////////////////////////////////////////////// - bfxERR parseWDconfParams(string ¶ms) + BfxClientErrors parseWDconfParams(string ¶ms) { using std::getline; using std::ifstream; diff --git a/include/bfx-api-cpp/error.hpp b/include/bfx-api-cpp/error.hpp index e8d7eb1..b1f9687 100644 --- a/include/bfx-api-cpp/error.hpp +++ b/include/bfx-api-cpp/error.hpp @@ -7,7 +7,7 @@ #pragma once -enum bfxERR +enum BfxClientErrors { noError = 0, curlERR, // 1 diff --git a/include/bfx-api-cpp/jsonutils.hpp b/include/bfx-api-cpp/jsonutils.hpp index 156bc5f..14ca7d9 100644 --- a/include/bfx-api-cpp/jsonutils.hpp +++ b/include/bfx-api-cpp/jsonutils.hpp @@ -192,7 +192,7 @@ namespace jsonutils cerr << "Invalid json - response:" << endl; cerr << inputJson << endl; cerr << "API endpoint: " << apiEndPoint << endl; - return bfxERR::responseParseError; + return BfxClientErrors::responseParseError; } // Create rapidjson validator and check for schema errors @@ -210,10 +210,10 @@ namespace jsonutils cerr << "Invalid document: " << sb.GetString() << endl; cerr << "Invalid response: " << inputJson << endl; cerr << "Invalid API endpoint: " << apiEndPoint << endl; - return bfxERR::responseSchemaError; + return BfxClientErrors::responseSchemaError; } - return bfxERR::noError; + return BfxClientErrors::noError; } private: @@ -280,7 +280,7 @@ namespace jsonutils // Routines //////////////////////////////////////////////////////////////////////////// - bfxERR jsonStrToUset(unordered_set &uSet, const string &inputJson) + BfxClientErrors jsonStrToUset(unordered_set &uSet, const string &inputJson) { // Create schema $ref resolver rj::Document sd; @@ -322,12 +322,12 @@ namespace jsonutils cerr << "Invalid document: " << sb.GetString() << endl; cerr << "Invalid response: " << inputJson << endl; } - return bfxERR::jsonStrToUSetError; + return BfxClientErrors::jsonStrToUSetError; } else { uSet.swap(handler.handlerUSet_); - return bfxERR::noError; + return BfxClientErrors::noError; } } } From d0f57b2faed4bf6adbf66a6404941efa28f1f67f Mon Sep 17 00:00:00 2001 From: Maple Mapleson Date: Tue, 17 Jul 2018 10:20:59 +0200 Subject: [PATCH 21/43] add rvalues arguments to doGet() and doPost() internal methods --- include/bfx-api-cpp/BitfinexAPI.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/bfx-api-cpp/BitfinexAPI.hpp b/include/bfx-api-cpp/BitfinexAPI.hpp index 029776a..57ba26e 100644 --- a/include/bfx-api-cpp/BitfinexAPI.hpp +++ b/include/bfx-api-cpp/BitfinexAPI.hpp @@ -1003,7 +1003,7 @@ namespace BfxAPI return noError; }; - void doGETrequest(const string &apiEndPoint, const string ¶ms) + void doGETrequest(string &&apiEndPoint, const string ¶ms) { bfxApiStatusCode_ = noError; @@ -1041,7 +1041,7 @@ namespace BfxAPI } }; - void doPOSTrequest(const string &apiEndPoint, const string ¶ms) + void doPOSTrequest(string &&apiEndPoint, const string ¶ms) { bfxApiStatusCode_ = noError; From 0e51419140d90cb7904c4092c42c877cd8c0b5ea Mon Sep 17 00:00:00 2001 From: Maple Mapleson Date: Sat, 21 Jul 2018 12:39:05 +0200 Subject: [PATCH 22/43] travis yaml initial --- .travis.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..771b625 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,20 @@ +language: cpp + +script: ./configure && make && make test + +compiler: + - clang + - gcc + +matrix: + include: + # works on Precise and Trusty + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-4.9 +env: +- MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" From 855789a9df77caf726abcfb87a091d377ce50561 Mon Sep 17 00:00:00 2001 From: Maple Mapleson Date: Tue, 24 Jul 2018 15:46:23 +0200 Subject: [PATCH 23/43] CMakeLists added, Logo added, code indentation fixed --- .gitignore | 2 + CMakeLists.txt | 39 ++++++++++ README.md | 34 ++++----- doc/logo/bfx-cpp-api_logo.png | Bin 0 -> 7177 bytes include/bfx-api-cpp/BitfinexAPI.hpp | 113 ++++++++++++++-------------- include/bfx-api-cpp/jsonutils.hpp | 17 +++-- src/example.cpp | 6 +- 7 files changed, 128 insertions(+), 83 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 doc/logo/bfx-cpp-api_logo.png diff --git a/.gitignore b/.gitignore index 0b7964c..85f5634 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .DS_Store +bin/ +build/ DerivedData/ bfx-cpp-api.xcodeproj/ include/.DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a826909 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,39 @@ +################################################################################ + +cmake_minimum_required (VERSION 3.12) +project (example) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin) + +################################################################################ + +# TARGET rapidjson +add_library(rapidjson INTERFACE) +target_include_directories(rapidjson INTERFACE "include/rapidjson") + +################################################################################ + +# TARGET bfxapicpp +add_library(bfxapicpp INTERFACE) +target_include_directories(bfxapicpp INTERFACE "include/bfx-api-cpp") +target_link_libraries(bfxapicpp INTERFACE rapidjson) + +################################################################################ + +# TARGET example +add_executable (example src/example.cpp) +target_include_directories (example PRIVATE include) +target_link_libraries(example +PUBLIC bfxapicpp +PRIVATE -lcryptopp -lcurl) +# Assuming example executable built into /bin directory configuration files +# will have following paths +target_compile_definitions(example PUBLIC +JSON_DEFINITIONS_FILE_PATH="${PROJECT_SOURCE_DIR}/doc/definitions.json" +WITHDRAWAL_CONF_FILE_PATH="${PROJECT_SOURCE_DIR}/doc/withdraw.conf") +# Enable all compiler warnings +target_compile_options(example PRIVATE -Wall) diff --git a/README.md b/README.md index cf17452..c02b2a5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,4 @@ -# bfx-cpp-api - -_C++ Bitfinex REST API client_ +![bfx-cpp-api logo](doc/logo/bfx-cpp-api_logo.png) *** @@ -13,12 +11,21 @@ _C++ Bitfinex REST API client_ This header-only library contains class for interfacing Bitfinex REST API v1. Current version supports response JSON schema validation. -### Installation +### Dependencies + +*bfx-cpp-api* depends on following external libraries + +* cmake - [https://cmake.org/download/](https://cmake.org/download/) +* libcryptopp - [https://www.cryptopp.com/](https://www.cryptopp.com/) +* libcurl - [https://curl.haxx.se/download.html](https://curl.haxx.se/download.html) -1. Copy content of `include/` directory into your project's `include/` directory and -add `#include "bfx-api-cpp/BitfinexAPI.hpp"` in your `.cpp` file. +### How to Build'n'Run `src/example.cpp` -2. Copy `doc/` directory with configuration files into your project. +1. Install dependencies (via apt, homebrew etc.). +2. Clone or download bfx-api-cpp repository. +3. Peek into `/src/example.cpp` +4. Create and run `Makefile` with `cd /build && cmake .. && make`. +5. Run `example` executable `./example`. ### Usage @@ -44,20 +51,9 @@ add `#include "bfx-api-cpp/BitfinexAPI.hpp"` in your `.cpp` file. See self-explanatory `src/example.cpp` for general usage and more requests. -### Dependencies - -*bfx-cpp-api* depends on following external libraries - -* libcryptopp - [https://www.cryptopp.com/](https://www.cryptopp.com/) -* libcurl - [https://curl.haxx.se/download.html](https://curl.haxx.se/download.html) - -### ToDo - -- [ ] Unit tests -- [x] JSON Scheme validation - ### Change Log +- 2018-07-24 CMakeLists.txt added. Installation instructions changed. - 2018-07-11 Schema validation logic complete. Client currently validates public requests only. ### Author diff --git a/doc/logo/bfx-cpp-api_logo.png b/doc/logo/bfx-cpp-api_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..cdcb7d5e52ea78994db6ec12294a6b5a303555b8 GIT binary patch literal 7177 zcmd^khdW$d*Y^xz5E&#A!h|6RqSw()^yn>0A~CvP45KG{)I^CMo#?#=5rV8Mz->*~IfoW7H`y@iu2!qFT6H?ub}hbq}2EX}pd5oYcVo#x^I06vqo zj=qz=s*0$oy&V_gj}Dic9qJkl0EkPup%A7v=1x!(b4zQa1mjNqcSfkSnFOPrpem0l zO2*vETG7MNT-!rU$JE2dRK$!?N)jsWCVEX^XYPc6y4l$x(V}h=jDPWpUdw;Fxf!8< zsW{n4F#gLaeN_#pjJ=~dRFI3G)0Bso7b+yg#Va5vB*e=B<>TSyf;ceLc@6%i5n!@N9M;q;=W&Yp({;$Mn9e0#Dx0X5D-r3Rgdg3e|{xO9Tm2osj zIN3Yu*xTFw9YqZ*dnbFel|2e7qb&qwRYjOuBmZ=;{e@6f6;(o_oe)SpFk_i2q64>&Aal-yC_J?~d1ry(FIS_4<7QXsGDOQPxkOl6X|Au!%hzwCw!dzbg2? z4eQo)er!>mU*F8_9kNEdCpMd~R(6lAv;cg1xlzR~6%FH)(@viLuJs*RLz95p5cb+1 z0%;XNUolm6-zMf3u;Xi#<$Y&o=Xa_H3(KqA>lZb1CJo!hfVwGmpB__%4CFPo49?YO zw{^Sw1=kFY)=lm<4ldtqThjZ`fax6&tm+03-&x+?+sCb2eQDh~JUzmlbuJzzf6iUl z94;K*#$8+<93DnBw3)=UHe%WUZe6g%Q8J@Ud7bC>n4deldqG8-U;2Kw^z>ynzZqIP z9@{(`p7`~kqBo}6?BEzTx44wn)E?KWxv{ymzCW$~(p;&uxnVResAXm6Xjvg+0@ges zrl>x>cho)p>viSag`az@tm2rDE-nk(0@_B#)B86!cXk58qA*jb)Meew0^&V` zLmxk-#JB0dikfb|>!Ydufti?2s2k;$R4DJ78(P?+4;#dt9|GTY4-Ab=t#1QvQ31Vr zx<gwwEpK@Ja0{!~0>(kRSe4pN{-&fLg{rxLP*N;oO ze{3J0H}>?k%w4w_oq@yYJv8D#S3eydsB z^sZ?x#$dymWGk>e*9J6Pwe7CIH~=RtHSNDF{{MReXrF+X0RT!aKW1lu5#$b66UlE(8@nKVz==}&^uohr8lpb>Apyd;}?l12DMW=_+7|Mu?t z6ilHQ2B&_F*9s(dkcLE(gZ?ie=r@S{lnM9ERoKv9H5g-~A*Z`Jp{r+vr>uQ4cqZ%v z6R1-8Ih}xuXvoj@bv2?U2!FoEKR=L;*F8;rr?y65ARu>0SKG9LJScE8!US8lS#2zB zLQVCC+4KJV0E&`OokO)@gRzxoIXa(?w>elw=18&pj<&K<5#?&s-3a9!pn8Rh)qBfAIMpoyTVidT*l&JE%gkE80hiA!4Hdx5J>QfWg^6p$YZI3}W} zCW%{!cnjcOzdFGp5KR*6x`DL{QX+4#0eU+16wsw z^#UhEMITa0Si*o0FL!;|dAa15e?t`4X%#C-y}*Y{wSQa0|5tMb}^q(UNLXl(jR`&TZ+0ZLPt{bhFi7l+pZoLDjUX zO=(sf5=->y!(@vXYV?L-F58{VqS0V=MpjmS`axvB#uFjSg5-hxAJ^uk$M zn%jq~MC0%sL_c{hD;W3zQep|5Bx>_c^j!pXAu?;Uv9iKrkF&8a91{FDCq8K6nb%IW zj$`$N75(rJ$?lniDcjT~?U9j!3@VbUEb_L{!3WuOUtoBO{?96w-q z0%a_dAcPG9pifD(8~rz+Oj|(uITYWsyIAU!FK_Rq>>dvAQg>C#^(Q>@&?A*um&$4O zU$G`#qto_XI3`CFE>&5tw>QH@sLp=ce2`Od=%ItGNUyFqMo8UfbO|*jm0e>=6@R8e zxfOe?#`J)H3!(^~ZY`X^B))N?-yo(T4uZ|A@L5u5C)<*l33Y4)6LlW{pOUKG z4b#IKy~A?-=wpz$48n$|Z1Pi)s!Nt~} zktsWWv7@k^ZeLbQ)zy-#H*i+=eWRyICZ*qk@IiiUL4bLzMu-C*o0l~>n9K7*le_0I z2t3Xk&hy5Enn`bakdno%1WM+~DM7~XM#L`|F>+WwtK!=^sC=&Jp-WvSepAI0m^!Qv zZgP<=zJRgn7IMT3N?PiSV|Ohqpan4|Rua{)IX`%%)mLp9b{=IjPMBxJ6gD@nt9(M9 zAR12B1em7dwE(Xfl=jS7xKB*><*N2ALm!vL2#J)2LxP2tRCH^9@roU5xPn^=zi7VZ z&*CLd)2|I`YB25lDGU;0rX&2w(RUA&aTMh*N3e@#b6EO7&{PL0A~cBJcujVLqSMFt zPbjNeysTq9-^_~37USOCNNFY5XM3^bl2+csEGTYDz7=PSTl+%$&X6)*r47ZGy)UDE z>4++pSV|U%6BgGnxXAfYMOx%=p z8Q+#F@H{whdD9) zQBGdBA_C`GXsr6qXKXyl>j;sL{z4`G(hpU*n|;Yl!lL)BgXiu=OHr~Kki~q_3B6vQ z1{-|!a@F`}hN|x3+2k3>4|UQp@tr(B?1)GHz{tl)l^HgH=91q|48`3a*Jg=GXjW6q zQl(hgP2)0ccTQ@56UhF}^z|UYvIZR1OcyNl)WgS`&`SJzB@Paj>nj5_JPKk+Ty6D( z3b#u3Rhd)mJYO80F=#DZjFeZoH%Ivi&dYP0%;k%{#1S*yq(t z8(V1?l_z)MX|mwz=|{;XWRDWo;svxnFB%iVSDH#K$dvg}-3-hU!oM|a?1l0fUA_-c zLjyOch;zqeas^mgI$5ogAw5uPNNE!yjCLWjx13!+%e3K`_!ni#=nxTGFeiCgH{8in zM?mb*=ISCW%%7Qv_s!S}G>~mG^p?1qorDx;xuHMZEX;cflla6#rB@&`9AdWgpn{4h zra@dSr=guxG#pCv7{r)F_Jk>-zFOLFd>PCAHTkNHFXfRp+kNWRoI5y9Uh-M3&56=B zR%z05StF4qb=Ha;txoU&D{b$$JR*=JQTZNo@sx9{8CL6Ugb~LVIJYF23nM{b4ekg% zy4toOv-vK~3|-kt2_Wll)QHu=s|f~=XPdruJw^7WYW3WdjjQC(atMf1 z@H=@EY_eO+NKy6_-DLhHOLx9HRhhHBVeL{2LelYh5R&&f9F@K?hfYs@H>7t#3c8ZOTpnh)X$LC`i#T+{RAi5!E=g&-jar1`Em6CA_DEa*A$3{Xa z%mdH6-n88wPIED3|5-;&Cry4~bnCl)X#QSp1ylF*O679oXnP=dIX^&nBt#_Z9z9h$ znS5w}k^idV`YzA;!p$Dto};1+*W@_S1t_|G?P=fH1Pscj+0kuLGom;>(4v^rfwvJ` z-K_j<$gS+`to|T=6Wg)cP~`EWZT3KU*B^F|`66r2hz6?ag!X z!!j)CBVJPwFI7^166%PFb7#jf^m=KrVJzHkKP}Ag!r8%XGfiEa^|8$`J3l;P;=&m< zP%8PNT_at(iiF;ereRLxkc8XDyj?n45fYr(fDrlMt@X?T?#rloY`jc2w0jGS`0_jF z*)vM3qz-kr4~dNk7zZ+rP0g3Z!A;O{>{c^E5tjBOyqze!vm761_{u5`+6B&wBRg4l3=%s4Ku!#(1~!U zt#)a;z202^9>k=fY0anm3=mEH;iO+U@l9??ZyzJ!Q48V-fsW5Q^|J8pXlm6a0JvJb z77z~#+yCk@%fs_=B^3ic!D}*O1_YYXxR+}KXo8)Eg2qq9N!we;(+8O{Nl=d_J8q`% zfuP{NC7yRD$w34hge=6q*pQ1Tt8`2!Br#yGXwHv`@V0DiofUN#(|VXc3;N3~Qu5Z+ z5*{zlGfIcNSKyfwJU<>Ka+oiJcP<+geAK~U%ThZ07|8lb0?}ei^f?L?creV}2A4`F zcPPU$JW3mV3{0pGSVA5je>lE#C6?VGrSVHf%Hm7Lw0WccWa(6U zP3n$ErSk@npN8c@cq!Fe;?Db52CeX%v9qn7^+TeAPiaDMFyhJr4eF$73$nOLG^V!_feu8H@APL6@QKf!t2JK*T>V^i+15S z*Q%Qj``OPD#`$=LDeI22T)y#?Jt~kFhJ%w>#1{kd5R+ps)%2SPG=N-8iw7;w7=rGV z^a^(;si#d(DhJg4CakMfi`V}3Ggp0v$K#dDoJ@n8)KPcXR>!j5E8g*#2eA_V6y#YrTPs9Bgw!$0vOKWa{=bccPns zSQ+qvypi06|CK|vN%uhP9vN;ID}d(<}=K}Ch?}?kS;9lRz4f<-j-BY68kU4FW=p||i-N=6Y z$hN}musd~`S}+6{lC4mcUQLzgOZ7XhnEze?~neRvSA_dTbJA$P+RiUg~7#vp#C23hj6i6*|H z_d|u<0(0uJVXVLdq|+x4l;3@&d${GhUZSrmRJ8!B5`IUrlkc216XI^Wkt9;pDg?Hf zhX~BE2#F8VkL=n3wFcfDCFF=49k4zy%K%cev+yT?Km)wz92@lDh`E zQG~7>1ydhS#!ADJCN05Ogcm(S5E1tn#oCmC*xa?9*1{xFP@k&5SkhG&KjFURe7<%i zJy)tb`>ZuC|Iv5x8&~S7Z@F6-s&MuaD|-BW0`{L7mrf5TKWw3w3G#LJW5Tx4c`RGP z*ocJz6SbRgBh!2?>xyQhUdQy8SCaGQ#wK|=-ObcZ4!D3e)Tgi)AiRX`W?PRh7J{c# zJ=O3=!60W6m2556&{gBxj=Of#-+~vUZRxkNn~X!2msw+%jf=_gL-vJdXuXD(i+VMq zy}b{YRvBYUUq!F%X1!Zz(i-!;_yr=vLC#h*AjWE-uZHMIkS>UAao9;;_E>p_lzO0o ztRHl;abI*o@Tr68`v>5fq|%#Pl67xR(rE>>zO&Xn$?z*!zTJ4e%B{`O@xAcaojtIu ztErE3GSqAbMCMNc#T8sZesh0Se^>wEM^wJ59Gdq}x;~t-X zA22pIpv|C{H!D@g*ciH8hCK7ut*@~;CHQeD@(z7DXt0{g?d>97b5jQyzmV9SoF>$= zIHys1n^;P(P8zgmqnMD&IxOTU zXB~7}7*TN{$s4@h<%~}Sv`Bv(OF`G!{(O*bx$w;RF>-3V`-8$_x%-QddJ>xU_mV|- zFT!=n$~qBY@l=>iJIu{?^_$;=GCCbu#ZGU=OOZ}qe#n;o=yq(EupIMUDjF-XchX<= z5gTA~SY{M!E_sQkT-Phmh|mjB1xJw9k9{vo5BT;rF*hekoi@nmtY!>R25H2;J!E3RnGF(1sC;@JzbcoR9Y>n?i?)+m zf2XEzgY-M3=$>N&z*|rb44mA=$5loU1T&=p$=(Fe3JSHKhHr(wdNi}DW+>fHyPq!N zwT%)>jiVBo7JbF6r&F_YWU;|mlz8T`@rz-v1il}G!;yDy&f)9nf80o}yg{)>mzKF} zN54+P{R&iQjXkz7HJZDpTW`~ygUz^WPm||JOD`MM@1j9GR7e)Ir0AG%LZ9%YgzqBY zqW(c3fdpR@N>upO#s~^C&6fPltzT- zopp*OJi-r``wKvc8G*t_q%R(7yHqFaoQ*tZ;QWL&yc9mpTl(EWvv)40!IU&}rF41s z63of90mGe(6`cvf!RDX7$??a=K0god2_Sbl+_FohYT)Pp-Q3-$uFk0K`(9RCs{vJe zFx9?(vjLWm`5>fT}giG<<7n;|g zhtS1%DqD~$N$P$X{dg3#O=~fhW+V?;_x0b~U%{f;a+8Os#tiig6j$!*1ct$2o+*17 z3zJa!L16Tu=t9(-(u=?bu}!_!<~qpPWn_1M@=a!GAK(Rtn5&t0YjfvL#^LeYO5e&A z(-mFU7xV?>{Ds21=CvL3TbB!9y5I-&`2kFT)kEw@aZ)i!c3H~x1&KBH)PEQUQ6pR9SSCw%YsB5lSL8(VTXvWYU^;d;gTe6C>Y%h6Z1~3fnZ1 z5o>%Bo&krEw|<$6qD;N@`_AjLz$*IYfJTdnBFTsHn_W%F@ literal 0 HcmV?d00001 diff --git a/include/bfx-api-cpp/BitfinexAPI.hpp b/include/bfx-api-cpp/BitfinexAPI.hpp index 57ba26e..69551d7 100644 --- a/include/bfx-api-cpp/BitfinexAPI.hpp +++ b/include/bfx-api-cpp/BitfinexAPI.hpp @@ -61,7 +61,7 @@ using std::vector; // CRYPTOPP_NO_GLOBAL_BYTE signals byte is at CryptoPP::byte -#if defined(CRYPTOPP_NO_GLOBAL_BYTE) +#ifdef CRYPTOPP_NO_GLOBAL_BYTE using CryptoPP::byte; #endif @@ -71,7 +71,6 @@ namespace BfxAPI class BitfinexAPI { - public: //////////////////////////////////////////////////////////////////////// // Class constants @@ -80,7 +79,9 @@ namespace BfxAPI static constexpr auto API_URL = "https://api.bitfinex.com/v1"; static constexpr auto CURL_TIMEOUT = 30L; static constexpr auto CURL_DEBUG_VERBOSE = 0L; - static constexpr auto WITHDRAWAL_CONF_FILE_PATH = "doc/withdraw.conf"; + #ifndef WITHDRAWAL_CONF_FILE_PATH + static constexpr auto WITHDRAWAL_CONF_FILE_PATH = "withdraw.conf"; + #endif //////////////////////////////////////////////////////////////////////// // Typedefs @@ -98,6 +99,8 @@ namespace BfxAPI using vOrders = vector; using vIds = vector; + public: + //////////////////////////////////////////////////////////////////////// // Constructor - Destructor //////////////////////////////////////////////////////////////////////// @@ -197,16 +200,16 @@ namespace BfxAPI // Getters const string getWDconfFilePath() const noexcept - { return WDconfFilePath_; } + { return WDconfFilePath_; } const BfxClientErrors& getBfxApiStatusCode() const noexcept - { return bfxApiStatusCode_; } + { return bfxApiStatusCode_; } const CURLcode& getCurlStatusCode() const noexcept - { return curlStatusCode_; } + { return curlStatusCode_; } const string& strResponse() const noexcept - { return result_ ; } + { return result_ ; } bool hasApiError() const noexcept { @@ -218,7 +221,7 @@ namespace BfxAPI // Setters constexpr void setWDconfFilePath(const string &path) noexcept - { WDconfFilePath_ = path; } + { WDconfFilePath_ = path; } constexpr void setKeys(const string &accessKey, const string &secretKey) noexcept { @@ -226,8 +229,8 @@ namespace BfxAPI secretKey_ = secretKey; } - constexpr string& getAccessKey() { return accessKey_;} - constexpr string& getSecretKey() { return secretKey_;} + constexpr string& getAccessKeyRef() { return accessKey_;} + constexpr string& getSecretKeyRef() { return secretKey_;} //////////////////////////////////////////////////////////////////////// // Public endpoints @@ -377,10 +380,10 @@ namespace BfxAPI const bool &renew = false) { if (!inArray(method, methods_)) - { bfxApiStatusCode_ = badDepositMethod; return *this; } - + { bfxApiStatusCode_ = badDepositMethod; return *this; } + if (!inArray(walletName, walletNames_)) - { bfxApiStatusCode_ = badWalletType; return *this; } + { bfxApiStatusCode_ = badWalletType; return *this; } string params = "{\"request\":\"/v1/deposit/new\",\"nonce\":\"" + getTonce() + "\""; @@ -429,11 +432,11 @@ namespace BfxAPI const string &walletto) { if (!inArray(currency, currencies_)) - { bfxApiStatusCode_ = badCurrency; return *this; } - + { bfxApiStatusCode_ = badCurrency; return *this; } + if (!inArray(walletfrom, walletNames_) || - !inArray(walletto, walletNames_)) - { bfxApiStatusCode_ = badWalletType; return *this; } + !inArray(walletto, walletNames_)) + { bfxApiStatusCode_ = badWalletType; return *this; } string params = "{\"request\":\"/v1/transfer\",\"nonce\":\"" + getTonce() + "\""; @@ -669,14 +672,14 @@ namespace BfxAPI { // Is currency valid ? if (!inArray(currency, currencies_)) - { bfxApiStatusCode_ = badCurrency; return *this; }; + { bfxApiStatusCode_ = badCurrency; return *this; }; // Is wallet type valid ? // Modified condition which accepts "all" value for all wallets // balances together.If "all" specified then there is simply no // wallet parameter in POST request. if (!inArray(walletType, walletNames_) || walletType != "all") - { bfxApiStatusCode_ = badWalletType; return *this; }; + { bfxApiStatusCode_ = badWalletType; return *this; }; string params = "{\"request\":\"/v1/history\",\"nonce\":\"" + getTonce() + "\""; @@ -700,10 +703,10 @@ namespace BfxAPI const unsigned &limit = 500) { if (!inArray(currency, currencies_)) - { bfxApiStatusCode_ = badCurrency; return *this; }; + { bfxApiStatusCode_ = badCurrency; return *this; }; if (!inArray(method, methods_) && method != "wire" && method != "all") - { bfxApiStatusCode_ = badDepositMethod; return *this; }; + { bfxApiStatusCode_ = badDepositMethod; return *this; }; string params = "{\"request\":\"/v1/history/movements\",\"nonce\":\"" + getTonce() + "\""; @@ -712,7 +715,7 @@ namespace BfxAPI params += ",\"method\":\"" + method + "\""; params += ",\"since\":\"" + to_string(since) + "\""; params += ",\"until\":\"" + - (!until ? getTonce() : to_string(until)) + "\""; + (!until ? getTonce() : to_string(until)) + "\""; params += ",\"limit\":" + to_string(limit); params += "}"; doPOSTrequest("/history/movements/", params); @@ -730,16 +733,16 @@ namespace BfxAPI bfxApiStatusCode_ = badSymbol; else { - string params = "{\"request\":\"/v1/mytrades\",\"nonce\":\"" + - getTonce() + "\""; - params += ",\"symbol\":\"" + symbol + "\""; - params += ",\"timestamp\":\"" + to_string(timestamp) + "\""; - params += ",\"until\":\"" + + string params = "{\"request\":\"/v1/mytrades\",\"nonce\":\"" + + getTonce() + "\""; + params += ",\"symbol\":\"" + symbol + "\""; + params += ",\"timestamp\":\"" + to_string(timestamp) + "\""; + params += ",\"until\":\"" + (!until ? getTonce() : to_string(until)) + "\""; - params += ",\"limit_trades\":" + to_string(limit_trades); - params += ",\"reverse\":" + to_string(reverse); - params += "}"; - doPOSTrequest("/mytrades/", params); + params += ",\"limit_trades\":" + to_string(limit_trades); + params += ",\"reverse\":" + to_string(reverse); + params += "}"; + doPOSTrequest("/mytrades/", params); } return *this; @@ -756,15 +759,15 @@ namespace BfxAPI bfxApiStatusCode_ = badCurrency; else { - string params = "{\"request\":\"/v1/offer/new\",\"nonce\":\"" + - getTonce() + "\""; - params += ",\"currency\":\"" + currency + "\""; - params += ",\"amount\":\"" + to_string(amount) + "\""; - params += ",\"rate\":\"" + to_string(rate) + "\""; - params += ",\"period\":" + to_string(period); - params += ",\"direction\":\"" + direction + "\""; - params += "}"; - doPOSTrequest("/offer/new/", params); + string params = "{\"request\":\"/v1/offer/new\",\"nonce\":\"" + + getTonce() + "\""; + params += ",\"currency\":\"" + currency + "\""; + params += ",\"amount\":\"" + to_string(amount) + "\""; + params += ",\"rate\":\"" + to_string(rate) + "\""; + params += ",\"period\":" + to_string(period); + params += ",\"direction\":\"" + direction + "\""; + params += "}"; + doPOSTrequest("/offer/new/", params); } return *this; @@ -773,7 +776,7 @@ namespace BfxAPI BitfinexAPI& cancelOffer(const long long &offer_id) { string params = "{\"request\":\"/v1/offer/cancel\",\"nonce\":\"" + - getTonce() + "\""; + getTonce() + "\""; params += ",\"offer_id\":" + to_string(offer_id); params += "}"; doPOSTrequest("/offer/cancel/", params); @@ -784,7 +787,7 @@ namespace BfxAPI BitfinexAPI& getOfferStatus(const long long &offer_id) { string params = "{\"request\":\"/v1/offer/status\",\"nonce\":\"" + - getTonce() + "\""; + getTonce() + "\""; params += ",\"offer_id\":" + to_string(offer_id); params += "}"; doPOSTrequest("/offer/status/", params); @@ -795,7 +798,7 @@ namespace BfxAPI BitfinexAPI& getActiveCredits() { string params = "{\"request\":\"/v1/credits\",\"nonce\":\"" + - getTonce() + "\""; + getTonce() + "\""; params += "}"; doPOSTrequest("/credits/", params); @@ -805,7 +808,7 @@ namespace BfxAPI BitfinexAPI& getOffers() { string params = "{\"request\":\"/v1/offers\",\"nonce\":\"" + - getTonce() + "\""; + getTonce() + "\""; params += "}"; doPOSTrequest("/offers/", params); @@ -815,7 +818,7 @@ namespace BfxAPI BitfinexAPI& getOffersHistory(const unsigned &limit) { string params = "{\"request\":\"/v1/offers/hist\",\"nonce\":\"" + - getTonce() + "\""; + getTonce() + "\""; params += ",\"limit\":" + to_string(limit); params += "}"; doPOSTrequest("/offers/hist/", params); @@ -838,7 +841,7 @@ namespace BfxAPI else { string params = "{\"request\":\"/v1/mytrades_funding\",\"nonce\":\"" - + getTonce() + "\""; + + getTonce() + "\""; // param inconsistency in BFX API, "symbol" should be currency params += ",\"symbol\":\"" + currency + "\""; params += ",\"until\":" + to_string(until); @@ -853,7 +856,7 @@ namespace BfxAPI BitfinexAPI& getTakenFunds() { string params = "{\"request\":\"/v1/taken_funds\",\"nonce\":\"" + - getTonce() + "\""; + getTonce() + "\""; params += "}"; doPOSTrequest("/taken_funds/", params); @@ -863,7 +866,7 @@ namespace BfxAPI BitfinexAPI& getUnusedTakenFunds() { string params = "{\"request\":\"/v1/unused_taken_funds\",\"nonce\":\"" - + getTonce() + "\""; + + getTonce() + "\""; params += "}"; doPOSTrequest("/unused_taken_funds/", params); @@ -873,7 +876,7 @@ namespace BfxAPI BitfinexAPI& getTotalTakenFunds() { string params = "{\"request\":\"/v1/total_taken_funds\",\"nonce\":\"" - + getTonce() + "\""; + + getTonce() + "\""; params += "}"; doPOSTrequest("/total_taken_funds/", params); @@ -883,7 +886,7 @@ namespace BfxAPI BitfinexAPI& closeLoan(const long long &offer_id) { string params = "{\"request\":\"/v1/funding/close\",\"nonce\":\"" + - getTonce() + "\""; + getTonce() + "\""; params += ",\"swap_id\":" + to_string(offer_id); params += "}"; doPOSTrequest("/funding/close/", params); @@ -894,7 +897,7 @@ namespace BfxAPI BitfinexAPI& closePosition(const long long &position_id) { string params = "{\"request\":\"/v1/position/close\",\"nonce\":\"" + - getTonce() + "\""; + getTonce() + "\""; params += ",\"position_id\":" + to_string(position_id); params += "}"; doPOSTrequest("/position/close/", params); @@ -914,16 +917,16 @@ namespace BfxAPI unordered_set methods_; // valid deposit methods unordered_set walletNames_; // valid walletTypes unordered_set types_; // valid Types (see new order endpoint) + // BitfinexAPI settings + string accessKey_, secretKey_; + string WDconfFilePath_; + string APIurl_; // CURL instances CURL *curlGET_; CURL *curlPOST_; CURLcode curlStatusCode_; // internal jsonutils instances jsonutils::BfxSchemaValidator schemaValidator_; - // BitfinexAPI settings - string WDconfFilePath_; - string APIurl_; - string accessKey_, secretKey_; // dynamic and status variables BfxClientErrors bfxApiStatusCode_; string result_; diff --git a/include/bfx-api-cpp/jsonutils.hpp b/include/bfx-api-cpp/jsonutils.hpp index 14ca7d9..2b9c23c 100644 --- a/include/bfx-api-cpp/jsonutils.hpp +++ b/include/bfx-api-cpp/jsonutils.hpp @@ -43,11 +43,16 @@ namespace jsonutils /// Helper class resolving remote schema for schema $ref operator class MyRemoteSchemaDocumentProvider: public rj::IRemoteSchemaDocumentProvider { + + #ifndef JSON_DEFINITIONS_FILE_PATH + static constexpr auto JSON_DEFINITIONS_FILE_PATH = "definitions.json"; + #endif + public: MyRemoteSchemaDocumentProvider() { - FILE *pFileIn = fopen("doc/definitions.json", "r"); // non-Windows use "r" + FILE *pFileIn = fopen(JSON_DEFINITIONS_FILE_PATH, "r"); // non-Windows use "r" char readBuffer[65536]; rj::FileReadStream fReadStream(pFileIn, readBuffer, sizeof(readBuffer)); rj::Document d; @@ -56,10 +61,10 @@ namespace jsonutils remoteSchemaDoc_ = new rj::SchemaDocument(d); }; -// ~MyRemoteSchemaDocumentProvider() -// { -// delete remoteSchemaDoc_; -// }; + // ~MyRemoteSchemaDocumentProvider() + // { + // delete remoteSchemaDoc_; + // }; private: @@ -215,7 +220,7 @@ namespace jsonutils return BfxClientErrors::noError; } - + private: MyRemoteSchemaDocumentProvider provider_; diff --git a/src/example.cpp b/src/example.cpp index 880ebca..53e9679 100644 --- a/src/example.cpp +++ b/src/example.cpp @@ -31,14 +31,14 @@ int main(int argc, char *argv[]) // BfxAPI::BitfinexAPI bfxAPI("accessKey", "secretKey"); // Load API keys from file - ifstream ifs("doc/key-secret", ifstream::in); + ifstream ifs("../doc/key-secret", ifstream::in); if (!ifs.is_open()) { cerr << "Can't open 'key-secret' file. " << endl; return 1; } - getline(ifs, bfxAPI.getAccessKey()); - getline(ifs, bfxAPI.getSecretKey()); + getline(ifs, bfxAPI.getAccessKeyRef()); + getline(ifs, bfxAPI.getSecretKeyRef()); ifs.close(); // Fetch API From 62143e6849ef519dd9e44eeecf46805db6bfaa5e Mon Sep 17 00:00:00 2001 From: Maple Mapleson Date: Tue, 24 Jul 2018 22:26:22 +0200 Subject: [PATCH 24/43] README edit --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c02b2a5..e5b0599 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,10 @@ schema validation. 1. Install dependencies (via apt, homebrew etc.). 2. Clone or download bfx-api-cpp repository. -3. Peek into `/src/example.cpp` -4. Create and run `Makefile` with `cd /build && cmake .. && make`. -5. Run `example` executable `./example`. +3. Peek into self-documented `/src/example.cpp`. +4. Create `build` directory `$ mkdir /build`. +5. Build `example` binary `$ cd /build && cmake .. && make`. +6. Run `example` binary `$ ./example`. ### Usage From 84d020376991b8133d58ea71c29a5f09421132c9 Mon Sep 17 00:00:00 2001 From: Maple Mapleson Date: Tue, 24 Jul 2018 22:37:09 +0200 Subject: [PATCH 25/43] readme indentation fix --- README.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e5b0599..5cb2643 100644 --- a/README.md +++ b/README.md @@ -39,16 +39,18 @@ schema validation. // Fetch data bfxAPI.getTicker("btcusd"); - // Check for errors - if (!bfxAPI.hasApiError()) - // Get response in string - cout << bfxAPI.strResponse() << endl; - else - { - // Inspect errors - cout << bfxAPI.getBfxApiStatusCode() << endl; - cout << bfxAPI.getCurlStatusCode() << endl; - } + // Check for errors + if (!bfxAPI.hasApiError()) + { + // Get response in string + cout << bfxAPI.strResponse() << endl; + } + else + { + // Inspect errors + cout << bfxAPI.getBfxApiStatusCode() << endl; + cout << bfxAPI.getCurlStatusCode() << endl; + } See self-explanatory `src/example.cpp` for general usage and more requests. From 69cda30bda5f007717e9258d47da1655e47c5473 Mon Sep 17 00:00:00 2001 From: Maple Mapleson Date: Wed, 25 Jul 2018 10:18:34 +0200 Subject: [PATCH 26/43] initial circleci dir setup --- .circleci/config.yml | 0 .travis.yml | 20 -------------------- _config.yml | 1 - 3 files changed, 21 deletions(-) create mode 100644 .circleci/config.yml delete mode 100644 .travis.yml delete mode 100644 _config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..e69de29 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 771b625..0000000 --- a/.travis.yml +++ /dev/null @@ -1,20 +0,0 @@ -language: cpp - -script: ./configure && make && make test - -compiler: - - clang - - gcc - -matrix: - include: - # works on Precise and Trusty - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-4.9 -env: -- MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" diff --git a/_config.yml b/_config.yml deleted file mode 100644 index c419263..0000000 --- a/_config.yml +++ /dev/null @@ -1 +0,0 @@ -theme: jekyll-theme-cayman \ No newline at end of file From 2557fe093a86c661d537c69e5050466dd4a6fc87 Mon Sep 17 00:00:00 2001 From: Maple Mapleson Date: Wed, 25 Jul 2018 10:33:26 +0200 Subject: [PATCH 27/43] circleci test --- .circleci/config.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index e69de29..35311a9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -0,0 +1,8 @@ +version: 2 + jobs: + build: + docker: + - image: circleci/ruby:2.4.1-jessie + steps: + - checkout + - run: echo "A first hello" From 59256d49989d7e2ad361c05f2ff872ba29f9a69e Mon Sep 17 00:00:00 2001 From: Petr Javorik Date: Wed, 25 Jul 2018 10:44:22 +0200 Subject: [PATCH 28/43] Update config.yml --- .circleci/config.yml | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 35311a9..402a3b2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,8 +1,22 @@ version: 2 +jobs: + one: + docker: + - image: circleci/ruby:2.4.1-jessie + steps: + - checkout + - run: echo "A first hello" + - run: sleep 25 + two: + docker: + - image: circleci/ruby:2.4.1-jessie + steps: + - checkout + - run: echo "A more familiar hi" + - run: sleep 15 +workflows: + version: 2 + one_and_two: jobs: - build: - docker: - - image: circleci/ruby:2.4.1-jessie - steps: - - checkout - - run: echo "A first hello" + - one + - two From e2f2fde5736cdee6719319df45e900aae7a0588f Mon Sep 17 00:00:00 2001 From: Petr Javorik Date: Wed, 25 Jul 2018 10:58:24 +0200 Subject: [PATCH 29/43] Update config.yml --- .circleci/config.yml | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 402a3b2..107267b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,17 +6,35 @@ jobs: steps: - checkout - run: echo "A first hello" - - run: sleep 25 + - run: mkdir -p my_workspace + - run: echo "Trying out workspaces" > my_workspace/echo-output + - persist_to_workspace: + # Must be an absolute path, or relative path from working_directory + root: my_workspace + # Must be relative path from root + paths: + - echo-output two: docker: - image: circleci/ruby:2.4.1-jessie steps: - checkout - - run: echo "A more familiar hi" - - run: sleep 15 + - run: echo "A more familiar hi" + - attach_workspace: + # Must be absolute path or relative path from working_directory + at: my_workspace + + - run: | + if [[ $(cat my_workspace/echo-output) == "Trying out workspaces" ]]; then + echo "It worked!"; + else + echo "Nope!"; exit 1 + fi workflows: version: 2 one_and_two: jobs: - one - - two + - two: + requires: + - one From 8ef77bd1d625318b1c7ee8387ac59127f9b9a942 Mon Sep 17 00:00:00 2001 From: Maple Mapleson Date: Wed, 25 Jul 2018 15:12:29 +0200 Subject: [PATCH 30/43] readme edit, circleci edit --- .circleci/config.yml | 64 +++++++++++++++++++------------------------- README.md | 9 +++++++ 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 107267b..019edfc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,40 +1,30 @@ version: 2 jobs: - one: - docker: - - image: circleci/ruby:2.4.1-jessie - steps: - - checkout - - run: echo "A first hello" - - run: mkdir -p my_workspace - - run: echo "Trying out workspaces" > my_workspace/echo-output - - persist_to_workspace: - # Must be an absolute path, or relative path from working_directory - root: my_workspace - # Must be relative path from root - paths: - - echo-output - two: - docker: - - image: circleci/ruby:2.4.1-jessie - steps: - - checkout - - run: echo "A more familiar hi" - - attach_workspace: - # Must be absolute path or relative path from working_directory - at: my_workspace - - - run: | - if [[ $(cat my_workspace/echo-output) == "Trying out workspaces" ]]; then - echo "It worked!"; - else - echo "Nope!"; exit 1 - fi + run_cmake: + docker: + - image: circleci/python:3.7.0-stretch-browsers + steps: + - checkout + - run: + name: Install cmake + command: sudo apt install cmake + - run: + name: Create build/ dir and Makefile + command: | + mkdir -p build && cd build && cmake .. + build: + docker: + - image: circleci/python:3.7.0-stretch-browsers + steps: + - checkout + - run: + name: Build project + command: cd build && make workflows: - version: 2 - one_and_two: - jobs: - - one - - two: - requires: - - one + version: 2 + build_project: + jobs: + - run_cmake + - build: + requires: + - run_cmake diff --git a/README.md b/README.md index 5cb2643..0e7c2fd 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,15 @@ *** +### Build status + +| [Linux][lin-link] | +| :---------------: | +| ![lin-badge] | + +[lin-badge]: https://circleci.com/gh/MMquant/bfx-cpp-api/tree/master.svg?style=svg "CircleCI build status" +[lin-link]: https://circleci.com/gh/MMquant/bfx-cpp-api "CircleCI build status" + ### Notice ***Master*** branch contains new version of the client. For old version checkout ***legacy*** branch. From 12b73d044e68d925ccd8106b2f746645b6c171e5 Mon Sep 17 00:00:00 2001 From: Maple Mapleson Date: Thu, 26 Jul 2018 10:22:28 +0200 Subject: [PATCH 31/43] circleci config edit --- .circleci/config.yml | 41 +++++++++++----------------------- .circleci/install_deps.sh | 11 +++++++++ .circleci/run-build-locally.sh | 10 +++++++++ 3 files changed, 34 insertions(+), 28 deletions(-) create mode 100755 .circleci/install_deps.sh create mode 100755 .circleci/run-build-locally.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 019edfc..8025746 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,30 +1,15 @@ version: 2 jobs: - run_cmake: - docker: - - image: circleci/python:3.7.0-stretch-browsers - steps: - - checkout - - run: - name: Install cmake - command: sudo apt install cmake - - run: - name: Create build/ dir and Makefile - command: | - mkdir -p build && cd build && cmake .. - build: - docker: - - image: circleci/python:3.7.0-stretch-browsers - steps: - - checkout - - run: - name: Build project - command: cd build && make -workflows: - version: 2 - build_project: - jobs: - - run_cmake - - build: - requires: - - run_cmake + build: + docker: + - image: circleci/buildpack-deps:stretch-curl-browsers + steps: + - checkout + - run: + name: Install dependencies + command: | + ./.circleci/install_deps.sh + - run: + name: Build project + command: | + echo "Hello world" diff --git a/.circleci/install_deps.sh b/.circleci/install_deps.sh new file mode 100755 index 0000000..776c3ca --- /dev/null +++ b/.circleci/install_deps.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +# This script establishes custom dependencies directory. +mkdir $HOME/custom_deps +export PATH=$PATH:$HOME/custom_deps +cd $HOME/custom_deps + +# CMake +curl -o cmake.tar.gz https://cmake.org/files/v3.12/cmake-3.12.0-Linux-x86_64.tar.gz +tar -xf cmake.tar.gz +ln -s cmake-3.12.0-Linux-x86_64/cmake cmake \ No newline at end of file diff --git a/.circleci/run-build-locally.sh b/.circleci/run-build-locally.sh new file mode 100755 index 0000000..d6c09c1 --- /dev/null +++ b/.circleci/run-build-locally.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +# This script is used to run CircleCI build on local machine without pushing through remote repo. + +curl --user ${CIRCLE_TOKEN}: \ + --request POST \ + --form revision=$1 \ + --form config=@config.yml \ + --form notify=false \ + https://circleci.com/api/v1.1/project/github/MMquant/bfx-cpp-api/tree/develop From 59b96a8d38fa17eab4c9f8dae74621e09dd094b9 Mon Sep 17 00:00:00 2001 From: Maple Mapleson Date: Thu, 26 Jul 2018 10:49:32 +0200 Subject: [PATCH 32/43] install_deps.sh edit --- .circleci/config.yml | 4 ++-- .circleci/install_deps.sh | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8025746..04d956b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,10 +6,10 @@ jobs: steps: - checkout - run: - name: Install dependencies + name: Install dependencies to ~/custom_deps directory command: | ./.circleci/install_deps.sh - run: name: Build project command: | - echo "Hello world" + mkdir build && cd build && cmake .. && make diff --git a/.circleci/install_deps.sh b/.circleci/install_deps.sh index 776c3ca..57c775d 100755 --- a/.circleci/install_deps.sh +++ b/.circleci/install_deps.sh @@ -4,8 +4,12 @@ mkdir $HOME/custom_deps export PATH=$PATH:$HOME/custom_deps cd $HOME/custom_deps +echo "~/custom_deps directory successfully created" # CMake -curl -o cmake.tar.gz https://cmake.org/files/v3.12/cmake-3.12.0-Linux-x86_64.tar.gz +echo "Installing CMake..." +curl -sS -o cmake.tar.gz https://cmake.org/files/v3.12/cmake-3.12.0-Linux-x86_64.tar.gz +echo "Extracting CMake..." tar -xf cmake.tar.gz -ln -s cmake-3.12.0-Linux-x86_64/cmake cmake \ No newline at end of file +ln -s cmake-3.12.0-Linux-x86_64/cmake cmake +echo "Done!" \ No newline at end of file From 358535f635c7e53ed02b7946989b8a87cb1aace1 Mon Sep 17 00:00:00 2001 From: Maple Mapleson Date: Thu, 26 Jul 2018 11:05:52 +0200 Subject: [PATCH 33/43] install_deps.sh edit --- .circleci/install_deps.sh | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.circleci/install_deps.sh b/.circleci/install_deps.sh index 57c775d..9fcdfa7 100755 --- a/.circleci/install_deps.sh +++ b/.circleci/install_deps.sh @@ -1,10 +1,15 @@ #!/usr/bin/env bash # This script establishes custom dependencies directory. +echo "Creating ~/custom_deps directory" mkdir $HOME/custom_deps -export PATH=$PATH:$HOME/custom_deps cd $HOME/custom_deps -echo "~/custom_deps directory successfully created" +echo "~/Adding custom_deps in $PATH" +export PATH=$PATH:$HOME/custom_deps +if ! [[ $HOME/custom_deps == *"$PATH"* ]]; then + echo "Couldn't add custom_deps dir to $PATH" + exit 1 +fi # CMake echo "Installing CMake..." @@ -12,4 +17,4 @@ curl -sS -o cmake.tar.gz https://cmake.org/files/v3.12/cmake-3.12.0-Linux-x86_64 echo "Extracting CMake..." tar -xf cmake.tar.gz ln -s cmake-3.12.0-Linux-x86_64/cmake cmake -echo "Done!" \ No newline at end of file +echo "Done!" From 26a88720fd1962298b4ab753f254da1eaaf04510 Mon Sep 17 00:00:00 2001 From: Maple Mapleson Date: Thu, 26 Jul 2018 12:03:18 +0200 Subject: [PATCH 34/43] Update .circleci --- .circleci/config.yml | 20 ++++++++++++++++++++ .circleci/install_deps.sh | 7 ++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 04d956b..661d100 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,11 +4,31 @@ jobs: docker: - image: circleci/buildpack-deps:stretch-curl-browsers steps: + - restore_cache: + keys: + - source-{{ .Branch }}-{{ .Revision }} + - checkout + + - save_cache: + key: source-{{ .Branch }}-{{ .Revision }} + paths: + - ".git" + + - restore_cache: + keys: custom_deps_tar_checksum-{{ checksum "custom_deps" }} + - run: name: Install dependencies to ~/custom_deps directory command: | ./.circleci/install_deps.sh + tar -cf custom_deps.tar custom_deps + + - save_cache: + keys: custom_deps_tar_checksum-{{ checksum "custom_deps" }} + paths: + - ~/custom_deps + - run: name: Build project command: | diff --git a/.circleci/install_deps.sh b/.circleci/install_deps.sh index 9fcdfa7..05b84b5 100755 --- a/.circleci/install_deps.sh +++ b/.circleci/install_deps.sh @@ -11,10 +11,11 @@ if ! [[ $HOME/custom_deps == *"$PATH"* ]]; then exit 1 fi -# CMake +## CMake echo "Installing CMake..." curl -sS -o cmake.tar.gz https://cmake.org/files/v3.12/cmake-3.12.0-Linux-x86_64.tar.gz echo "Extracting CMake..." -tar -xf cmake.tar.gz -ln -s cmake-3.12.0-Linux-x86_64/cmake cmake +# delete archive so that it's not being cache "checksummed" by circleci +tar -xf cmake.tar.gz && rm cmake.tar.gz +ln -s cmake-3.12.0-Linux-x86_64/bin/cmake cmake echo "Done!" From 6eef12b68fb865bc0103fdd858d582409cbe7916 Mon Sep 17 00:00:00 2001 From: Maple Mapleson Date: Thu, 26 Jul 2018 12:05:31 +0200 Subject: [PATCH 35/43] Update .circleci --- .circleci/config.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 661d100..a345cf0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,6 +4,7 @@ jobs: docker: - image: circleci/buildpack-deps:stretch-curl-browsers steps: + - restore_cache: keys: - source-{{ .Branch }}-{{ .Revision }} @@ -25,7 +26,7 @@ jobs: tar -cf custom_deps.tar custom_deps - save_cache: - keys: custom_deps_tar_checksum-{{ checksum "custom_deps" }} + key: custom_deps_tar_checksum-{{ checksum "custom_deps" }} paths: - ~/custom_deps From 36805c4430aeeb8f1b9443753ae3d6322acb076e Mon Sep 17 00:00:00 2001 From: Maple Mapleson Date: Thu, 26 Jul 2018 12:11:46 +0200 Subject: [PATCH 36/43] Update .circleci --- .circleci/config.yml | 4 ++-- .circleci/install_deps.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a345cf0..1254a1c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -17,7 +17,7 @@ jobs: - ".git" - restore_cache: - keys: custom_deps_tar_checksum-{{ checksum "custom_deps" }} + keys: custom_deps_tar_checksum-{{ checksum "custom_deps.tar" }} - run: name: Install dependencies to ~/custom_deps directory @@ -26,7 +26,7 @@ jobs: tar -cf custom_deps.tar custom_deps - save_cache: - key: custom_deps_tar_checksum-{{ checksum "custom_deps" }} + key: custom_deps_tar_checksum-{{ checksum "custom_deps.tar" }} paths: - ~/custom_deps diff --git a/.circleci/install_deps.sh b/.circleci/install_deps.sh index 05b84b5..b86e81f 100755 --- a/.circleci/install_deps.sh +++ b/.circleci/install_deps.sh @@ -6,7 +6,7 @@ mkdir $HOME/custom_deps cd $HOME/custom_deps echo "~/Adding custom_deps in $PATH" export PATH=$PATH:$HOME/custom_deps -if ! [[ $HOME/custom_deps == *"$PATH"* ]]; then +if [[ $HOME/custom_deps != *"$PATH"* ]]; then echo "Couldn't add custom_deps dir to $PATH" exit 1 fi From 45f57bfe9708933478434d7557fa06af7a185768 Mon Sep 17 00:00:00 2001 From: Maple Mapleson Date: Thu, 26 Jul 2018 12:52:26 +0200 Subject: [PATCH 37/43] Update CircleCI --- .circleci/config.yml | 1 + .circleci/install_deps.sh | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1254a1c..3538b42 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,6 +8,7 @@ jobs: - restore_cache: keys: - source-{{ .Branch }}-{{ .Revision }} + - source-{{ .Branch }}- - checkout diff --git a/.circleci/install_deps.sh b/.circleci/install_deps.sh index b86e81f..e8def50 100755 --- a/.circleci/install_deps.sh +++ b/.circleci/install_deps.sh @@ -4,10 +4,10 @@ echo "Creating ~/custom_deps directory" mkdir $HOME/custom_deps cd $HOME/custom_deps -echo "~/Adding custom_deps in $PATH" +echo "~/Adding custom_deps in \$PATH" export PATH=$PATH:$HOME/custom_deps -if [[ $HOME/custom_deps != *"$PATH"* ]]; then - echo "Couldn't add custom_deps dir to $PATH" +if [[ $PATH != *"$HOME/custom_deps"* ]]; then + echo "Couldn't add custom_deps dir in $PATH" exit 1 fi From b6eb4f1957ace3bcc14831682a0f3f23618a7ed9 Mon Sep 17 00:00:00 2001 From: Maple Mapleson Date: Tue, 31 Jul 2018 09:36:27 +0200 Subject: [PATCH 38/43] update .circleci --- .circleci/config.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3538b42..ec5cd8b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -24,14 +24,22 @@ jobs: name: Install dependencies to ~/custom_deps directory command: | ./.circleci/install_deps.sh - tar -cf custom_deps.tar custom_deps + tar -cf custom_deps.tar ~/custom_deps - save_cache: key: custom_deps_tar_checksum-{{ checksum "custom_deps.tar" }} paths: - ~/custom_deps + - run: + name: Install apt dependencies + command: | + sudo apt-get install build-essential + sudo apt-get install libcurl4-gnutls-dev + sudo apt-get install libcrypto++6 + - run: name: Build project command: | + export PATH=$PATH:~/custom_deps mkdir build && cd build && cmake .. && make From 465b0795de7cc3c85eea65b04cd86d6c2bfab899 Mon Sep 17 00:00:00 2001 From: Maple Mapleson Date: Tue, 31 Jul 2018 16:50:06 +0200 Subject: [PATCH 39/43] custom docker development image minimum cmake ver 3.7 Updated README.md --- .circleci/config.yml | 24 +----------------------- .circleci/install_deps.sh | 21 --------------------- CMakeLists.txt | 2 +- README.md | 12 +++++++++--- 4 files changed, 11 insertions(+), 48 deletions(-) delete mode 100755 .circleci/install_deps.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index ec5cd8b..cca3816 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2 jobs: build: docker: - - image: circleci/buildpack-deps:stretch-curl-browsers + - image: mmquant/cpp_dev:deb9.5 steps: - restore_cache: @@ -17,29 +17,7 @@ jobs: paths: - ".git" - - restore_cache: - keys: custom_deps_tar_checksum-{{ checksum "custom_deps.tar" }} - - - run: - name: Install dependencies to ~/custom_deps directory - command: | - ./.circleci/install_deps.sh - tar -cf custom_deps.tar ~/custom_deps - - - save_cache: - key: custom_deps_tar_checksum-{{ checksum "custom_deps.tar" }} - paths: - - ~/custom_deps - - - run: - name: Install apt dependencies - command: | - sudo apt-get install build-essential - sudo apt-get install libcurl4-gnutls-dev - sudo apt-get install libcrypto++6 - - run: name: Build project command: | - export PATH=$PATH:~/custom_deps mkdir build && cd build && cmake .. && make diff --git a/.circleci/install_deps.sh b/.circleci/install_deps.sh deleted file mode 100755 index e8def50..0000000 --- a/.circleci/install_deps.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash - -# This script establishes custom dependencies directory. -echo "Creating ~/custom_deps directory" -mkdir $HOME/custom_deps -cd $HOME/custom_deps -echo "~/Adding custom_deps in \$PATH" -export PATH=$PATH:$HOME/custom_deps -if [[ $PATH != *"$HOME/custom_deps"* ]]; then - echo "Couldn't add custom_deps dir in $PATH" - exit 1 -fi - -## CMake -echo "Installing CMake..." -curl -sS -o cmake.tar.gz https://cmake.org/files/v3.12/cmake-3.12.0-Linux-x86_64.tar.gz -echo "Extracting CMake..." -# delete archive so that it's not being cache "checksummed" by circleci -tar -xf cmake.tar.gz && rm cmake.tar.gz -ln -s cmake-3.12.0-Linux-x86_64/bin/cmake cmake -echo "Done!" diff --git a/CMakeLists.txt b/CMakeLists.txt index a826909..16d700b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ ################################################################################ -cmake_minimum_required (VERSION 3.12) +cmake_minimum_required (VERSION 3.7) project (example) set(CMAKE_CXX_STANDARD 17) diff --git a/README.md b/README.md index 0e7c2fd..7d0c5ea 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,13 @@ schema validation. ### Dependencies -*bfx-cpp-api* depends on following external libraries +*bfx-cpp-api* depends on following external libraries/packages * cmake - [https://cmake.org/download/](https://cmake.org/download/) -* libcryptopp - [https://www.cryptopp.com/](https://www.cryptopp.com/) -* libcurl - [https://curl.haxx.se/download.html](https://curl.haxx.se/download.html) +* libcrypto++ - [https://www.cryptopp.com/](https://www.cryptopp.com/) +* libcrypto++-dev - [https://www.cryptopp.com/](https://www.cryptopp.com/) +* curl - [https://curl.haxx.se/download.html](https://curl.haxx.se/download.html) +* libcurl4-gnutls-dev or libcurl4-openssl-dev - [https://curl.haxx.se/download.html](https://curl.haxx.se/download.html) ### How to Build'n'Run `src/example.cpp` @@ -68,6 +70,10 @@ See self-explanatory `src/example.cpp` for general usage and more requests. - 2018-07-24 CMakeLists.txt added. Installation instructions changed. - 2018-07-11 Schema validation logic complete. Client currently validates public requests only. +### Known issues + +You will not be able to compile *bfx-cpp-api* with GCC<7.2 due to this [bug](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66297). + ### Author Petr Javorik [www.mmquant.net](www.mmquant.net) [maple@mmquant.net](maple@mmquant.net) From e7783441ff760ff4213f54dced24ffca496289b9 Mon Sep 17 00:00:00 2001 From: Maple Mapleson Date: Tue, 31 Jul 2018 16:51:59 +0200 Subject: [PATCH 40/43] cmake language dialect changed to 14 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 16d700b..afba25c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required (VERSION 3.7) project (example) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) From da60e4aca1998bf5eb8df12fadc39c5d806ff865 Mon Sep 17 00:00:00 2001 From: Maple Mapleson Date: Wed, 1 Aug 2018 17:00:45 +0200 Subject: [PATCH 41/43] Update Readme.md --- README.md | 72 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 7d0c5ea..81a5138 100644 --- a/README.md +++ b/README.md @@ -24,22 +24,71 @@ schema validation. *bfx-cpp-api* depends on following external libraries/packages -* cmake - [https://cmake.org/download/](https://cmake.org/download/) -* libcrypto++ - [https://www.cryptopp.com/](https://www.cryptopp.com/) -* libcrypto++-dev - [https://www.cryptopp.com/](https://www.cryptopp.com/) -* curl - [https://curl.haxx.se/download.html](https://curl.haxx.se/download.html) -* libcurl4-gnutls-dev or libcurl4-openssl-dev - [https://curl.haxx.se/download.html](https://curl.haxx.se/download.html) +* *cmake* - [https://cmake.org/download/](https://cmake.org/download/) +* *libcrypto++* - [https://www.cryptopp.com/](https://www.cryptopp.com/) +* *libcrypto++-dev* - [https://www.cryptopp.com/](https://www.cryptopp.com/) +* *curl* - [https://curl.haxx.se/download.html](https://curl.haxx.se/download.html) +* *libcurl4-gnutls-dev* or *libcurl4-openssl-dev* - [https://curl.haxx.se/download.html](https://curl.haxx.se/download.html) +* *gcc* > 7.2 or C++14 compatible *clang* ### How to Build'n'Run `src/example.cpp` 1. Install dependencies (via apt, homebrew etc.). -2. Clone or download bfx-api-cpp repository. -3. Peek into self-documented `/src/example.cpp`. -4. Create `build` directory `$ mkdir /build`. -5. Build `example` binary `$ cd /build && cmake .. && make`. -6. Run `example` binary `$ ./example`. +2. Clone or download *bfx-api-cpp* repository. +3. Add `key-secret` file in `bfx-api-cpp/doc` directory. (or edit `example.cpp` so that it doesn't use `key-secret` file) +4. Peek into self-documented `/src/example.cpp`. +5. Create `build` directory -### Usage + `$ mkdir /build` + +6. Build `example` binary + + `$ cd /build && cmake .. && make` + +7. Run `example` binary + + `$ ./example` + +### How to Build'n'Run `src/example.cpp` in Docker container + +Alternatively you can build `example.cpp` in custom `mmquant/dev_cpp:deb9.5` Docker container with all dependecies preinstalled. (This image is used also by CircleCI builds). This image is for development purposes only - do not use it in production. + +1. Pull the image from Docker cloud + + `$ docker pull mmquant/cpp_dev:deb9.5` + +2. Create container + + `$ docker run -dit mmquant/cpp_dev:deb9.5` + +3. Spawn bash + + `$ docker exec -it /bin/bash` + +4. Clone *bfx-cpp-api* repository into the container + + `# mkdir ~/project && cd ~/project && git clone https://github.com/MMquant/bfx-cpp-api.git` + +5. Add `key-secret` file in `bfx-api-cpp/doc` directory. (or edit `example.cpp` so that it doesn't use `key-secret` file) + + ``` + # echo > key-secret + # echo >> key-secret + ``` + +6. Build `example.cpp` + + ``` + # mkdir ~/project/bfx-cpp-api/build + # cd ~/project/bfx-cpp-api/build + # cmake .. && make + ``` +7. Run `example` binary + + `$ ./example` + + +### Quick interface overview // Create API client for both authenticated and unauthenticated requests BfxAPI::BitfinexAPI bfxAPI("accessKey", "secretKey"); @@ -67,6 +116,7 @@ See self-explanatory `src/example.cpp` for general usage and more requests. ### Change Log +- 2018-08-01 Dockerfile added. CircleCI added. - 2018-07-24 CMakeLists.txt added. Installation instructions changed. - 2018-07-11 Schema validation logic complete. Client currently validates public requests only. From f643fd3fb1d1f6e5ffed1d59ead9183c46c646ff Mon Sep 17 00:00:00 2001 From: Maple Mapleson Date: Wed, 1 Aug 2018 17:05:27 +0200 Subject: [PATCH 42/43] Dockerfile added --- Docker/Dockerfile | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 Docker/Dockerfile diff --git a/Docker/Dockerfile b/Docker/Dockerfile new file mode 100644 index 0000000..4bb49f8 --- /dev/null +++ b/Docker/Dockerfile @@ -0,0 +1,13 @@ +FROM debian:9.5 +RUN apt update && apt install -y \ + cmake \ + clang \ + git \ + libcrypto++ \ + libcrypto++-dev \ + curl \ + libcurl4-gnutls-dev \ + ssh \ + tar \ + gzip \ + ca-certificates From dc03fedb0b16d407adb5313ea64f7c35280cadd1 Mon Sep 17 00:00:00 2001 From: Petr Javorik Date: Fri, 9 Nov 2018 12:21:46 +0100 Subject: [PATCH 43/43] Develop merge (#11) * Removing not needed files. * Moving project source into app folder. * Adding docker files for alpine distro. * Updating README.md. * Updating git ignore. * Code improvement (#9) * Removing not needed files. * Moving project source into app folder. * Adding docker files for alpine distro. * Adding test. * Moving all the curl calls and settings to the Request object. * Formatting header. * Making key-secret file optional. * circleci fix --- .circleci/config.yml | 9 +- .circleci/images/Dockerfile | 16 + .circleci/run-build-locally.sh | 2 +- .gitignore | 6 +- Docker/Dockerfile | 13 - Dockerfile | 43 ++ README.md | 134 ++-- CMakeLists.txt => app/CMakeLists.txt | 16 + app/build/.gitignore | 2 + {doc => app/doc}/definitions.json | 0 {doc => app/doc}/logo/bfx-cpp-api_logo.png | Bin {doc => app/doc}/withdraw.conf | 0 .../include}/bfx-api-cpp/BitfinexAPI.hpp | 647 +++++++----------- app/include/bfx-api-cpp/HTTPRequest.hpp | 287 ++++++++ .../include}/bfx-api-cpp/error.hpp | 0 .../include}/bfx-api-cpp/jsonutils.hpp | 0 .../include}/rapidjson/allocators.h | 0 .../include}/rapidjson/cursorstreamwrapper.h | 0 {include => app/include}/rapidjson/document.h | 0 .../include}/rapidjson/encodedstream.h | 0 .../include}/rapidjson/encodings.h | 0 {include => app/include}/rapidjson/error/en.h | 0 .../include}/rapidjson/error/error.h | 0 .../include}/rapidjson/filereadstream.h | 0 .../include}/rapidjson/filewritestream.h | 0 {include => app/include}/rapidjson/fwd.h | 0 .../include}/rapidjson/internal/biginteger.h | 0 .../include}/rapidjson/internal/diyfp.h | 0 .../include}/rapidjson/internal/dtoa.h | 0 .../include}/rapidjson/internal/ieee754.h | 0 .../include}/rapidjson/internal/itoa.h | 0 .../include}/rapidjson/internal/meta.h | 0 .../include}/rapidjson/internal/pow10.h | 0 .../include}/rapidjson/internal/regex.h | 0 .../include}/rapidjson/internal/stack.h | 0 .../include}/rapidjson/internal/strfunc.h | 0 .../include}/rapidjson/internal/strtod.h | 0 .../include}/rapidjson/internal/swap.h | 0 .../include}/rapidjson/istreamwrapper.h | 0 .../include}/rapidjson/memorybuffer.h | 0 .../include}/rapidjson/memorystream.h | 0 .../include}/rapidjson/msinttypes/inttypes.h | 0 .../include}/rapidjson/msinttypes/stdint.h | 0 .../include}/rapidjson/ostreamwrapper.h | 0 {include => app/include}/rapidjson/pointer.h | 0 .../include}/rapidjson/prettywriter.h | 0 .../include}/rapidjson/rapidjson.h | 0 {include => app/include}/rapidjson/reader.h | 0 {include => app/include}/rapidjson/schema.h | 0 {include => app/include}/rapidjson/stream.h | 0 .../include}/rapidjson/stringbuffer.h | 0 {include => app/include}/rapidjson/writer.h | 0 {src => app/src}/example.cpp | 37 +- app/src/test.cpp | 72 ++ docker-compose.yml | 11 + docker/entrypoint.sh | 7 + docker/etc/supervisord.conf | 36 + 57 files changed, 833 insertions(+), 505 deletions(-) create mode 100644 .circleci/images/Dockerfile mode change 100755 => 100644 .circleci/run-build-locally.sh delete mode 100644 Docker/Dockerfile create mode 100644 Dockerfile rename CMakeLists.txt => app/CMakeLists.txt (69%) create mode 100644 app/build/.gitignore rename {doc => app/doc}/definitions.json (100%) rename {doc => app/doc}/logo/bfx-cpp-api_logo.png (100%) rename {doc => app/doc}/withdraw.conf (100%) rename {include => app/include}/bfx-api-cpp/BitfinexAPI.hpp (70%) create mode 100644 app/include/bfx-api-cpp/HTTPRequest.hpp rename {include => app/include}/bfx-api-cpp/error.hpp (100%) rename {include => app/include}/bfx-api-cpp/jsonutils.hpp (100%) rename {include => app/include}/rapidjson/allocators.h (100%) rename {include => app/include}/rapidjson/cursorstreamwrapper.h (100%) rename {include => app/include}/rapidjson/document.h (100%) rename {include => app/include}/rapidjson/encodedstream.h (100%) rename {include => app/include}/rapidjson/encodings.h (100%) rename {include => app/include}/rapidjson/error/en.h (100%) rename {include => app/include}/rapidjson/error/error.h (100%) rename {include => app/include}/rapidjson/filereadstream.h (100%) rename {include => app/include}/rapidjson/filewritestream.h (100%) rename {include => app/include}/rapidjson/fwd.h (100%) rename {include => app/include}/rapidjson/internal/biginteger.h (100%) rename {include => app/include}/rapidjson/internal/diyfp.h (100%) rename {include => app/include}/rapidjson/internal/dtoa.h (100%) rename {include => app/include}/rapidjson/internal/ieee754.h (100%) rename {include => app/include}/rapidjson/internal/itoa.h (100%) rename {include => app/include}/rapidjson/internal/meta.h (100%) rename {include => app/include}/rapidjson/internal/pow10.h (100%) rename {include => app/include}/rapidjson/internal/regex.h (100%) rename {include => app/include}/rapidjson/internal/stack.h (100%) rename {include => app/include}/rapidjson/internal/strfunc.h (100%) rename {include => app/include}/rapidjson/internal/strtod.h (100%) rename {include => app/include}/rapidjson/internal/swap.h (100%) rename {include => app/include}/rapidjson/istreamwrapper.h (100%) rename {include => app/include}/rapidjson/memorybuffer.h (100%) rename {include => app/include}/rapidjson/memorystream.h (100%) rename {include => app/include}/rapidjson/msinttypes/inttypes.h (100%) rename {include => app/include}/rapidjson/msinttypes/stdint.h (100%) rename {include => app/include}/rapidjson/ostreamwrapper.h (100%) rename {include => app/include}/rapidjson/pointer.h (100%) rename {include => app/include}/rapidjson/prettywriter.h (100%) rename {include => app/include}/rapidjson/rapidjson.h (100%) rename {include => app/include}/rapidjson/reader.h (100%) rename {include => app/include}/rapidjson/schema.h (100%) rename {include => app/include}/rapidjson/stream.h (100%) rename {include => app/include}/rapidjson/stringbuffer.h (100%) rename {include => app/include}/rapidjson/writer.h (100%) rename {src => app/src}/example.cpp (94%) create mode 100644 app/src/test.cpp create mode 100644 docker-compose.yml create mode 100755 docker/entrypoint.sh create mode 100644 docker/etc/supervisord.conf diff --git a/.circleci/config.yml b/.circleci/config.yml index cca3816..c1eeb82 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2 jobs: build: docker: - - image: mmquant/cpp_dev:deb9.5 + - image: mmquant/bfx-cpp-api-circleci-alpine:0.0.1 steps: - restore_cache: @@ -20,4 +20,9 @@ jobs: - run: name: Build project command: | - mkdir build && cd build && cmake .. && make + cd ~/project/app/build && cmake .. && make + + - run: + name : Test + command: | + cd ~/project/app/bin && ./test diff --git a/.circleci/images/Dockerfile b/.circleci/images/Dockerfile new file mode 100644 index 0000000..e496c60 --- /dev/null +++ b/.circleci/images/Dockerfile @@ -0,0 +1,16 @@ +FROM alpine:edge as runtime + +# Environment variables +ENV ESSENTIAL_PACKAGES="cmake clang curl curl-dev gcc g++ git gzip make mlocate openssh py-pip tar supervisor" \ + UTILITY_PACKAGES="nano vim ca-certificates" + +# Configure essential and utility packages +RUN apk update && \ + apk --no-cache --progress add $ESSENTIAL_PACKAGES $UTILITY_PACKAGES && \ + pip install --upgrade pip && \ + pip install supervisor-stdout + +# compiling https://github.com/weidai11/cryptopp.git +RUN cd /tmp && \ + git clone https://github.com/weidai11/cryptopp.git && \ + cd cryptopp && make && make install diff --git a/.circleci/run-build-locally.sh b/.circleci/run-build-locally.sh old mode 100755 new mode 100644 index d6c09c1..67b5b59 --- a/.circleci/run-build-locally.sh +++ b/.circleci/run-build-locally.sh @@ -7,4 +7,4 @@ curl --user ${CIRCLE_TOKEN}: \ --form revision=$1 \ --form config=@config.yml \ --form notify=false \ - https://circleci.com/api/v1.1/project/github/MMquant/bfx-cpp-api/tree/develop +https://circleci.com/api/v1.1/project/github/MMquant/bfx-cpp-api/tree/develop diff --git a/.gitignore b/.gitignore index 85f5634..822ea4a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ .DS_Store -bin/ -build/ +app/bin/ DerivedData/ bfx-cpp-api.xcodeproj/ include/.DS_Store include/rapidjson/.DS_Store -key-secret +app/doc/key-secret +app/.ash_history diff --git a/Docker/Dockerfile b/Docker/Dockerfile deleted file mode 100644 index 4bb49f8..0000000 --- a/Docker/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM debian:9.5 -RUN apt update && apt install -y \ - cmake \ - clang \ - git \ - libcrypto++ \ - libcrypto++-dev \ - curl \ - libcurl4-gnutls-dev \ - ssh \ - tar \ - gzip \ - ca-certificates diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9ba5adf --- /dev/null +++ b/Dockerfile @@ -0,0 +1,43 @@ +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Stage 1 - Installing essential and utility pkgs. +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +FROM alpine:edge as runtime + +# Environment variables +ENV ESSENTIAL_PACKAGES="cmake clang curl curl-dev gcc g++ git gzip make mlocate openssh py-pip tar supervisor" \ + UTILITY_PACKAGES="nano vim ca-certificates" + +# Configure essential and utility packages +RUN apk update && \ + apk --no-cache --progress add $ESSENTIAL_PACKAGES $UTILITY_PACKAGES && \ + pip install --upgrade pip && \ + pip install supervisor-stdout + +# compiling https://github.com/weidai11/cryptopp.git +RUN cd /tmp && \ + git clone https://github.com/weidai11/cryptopp.git && \ + cd cryptopp && make && make install + +RUN mkdir -p /home/bfx-cpp-api + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Stage 2 - Applying needed configurations. +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +COPY ./docker/etc /etc + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Stage 3 - Adding project files into VM. +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +# Adding project folder and needed files +ADD ./app /home/bfx-cpp-api/app +RUN chmod -R a+w /home/bfx-cpp-api/app + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# Stage 4 - Adding entry point. +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +# Adding entry point +ADD ./docker/entrypoint.sh /sbin/entrypoint.sh diff --git a/README.md b/README.md index 81a5138..de5f336 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![bfx-cpp-api logo](doc/logo/bfx-cpp-api_logo.png) +![bfx-cpp-api logo](app/doc/logo/bfx-cpp-api_logo.png) *** @@ -35,87 +35,98 @@ schema validation. 1. Install dependencies (via apt, homebrew etc.). 2. Clone or download *bfx-api-cpp* repository. -3. Add `key-secret` file in `bfx-api-cpp/doc` directory. (or edit `example.cpp` so that it doesn't use `key-secret` file) -4. Peek into self-documented `/src/example.cpp`. -5. Create `build` directory +3. Add `key-secret` file in `bfx-api-cpp/app/doc` directory. (or edit `example.cpp` so that it doesn't use `key-secret` file) +4. Peek into self-documented `/app/src/example.cpp`. +5. Build `example` binary - `$ mkdir /build` +```BASH +cd app/build && cmake .. && make +``` -6. Build `example` binary +7. Run `example` binary from `app/bin` - `$ cd /build && cmake .. && make` - -7. Run `example` binary - - `$ ./example` +```BASH +./example +``` ### How to Build'n'Run `src/example.cpp` in Docker container -Alternatively you can build `example.cpp` in custom `mmquant/dev_cpp:deb9.5` Docker container with all dependecies preinstalled. (This image is used also by CircleCI builds). This image is for development purposes only - do not use it in production. +1. Clone or download *bfx-api-cpp* repository. +2. Build docker image -1. Pull the image from Docker cloud +```BASH +cd +docker-compose build +``` - `$ docker pull mmquant/cpp_dev:deb9.5` +3. Start docker image -2. Create container +```BASH +docker-compose up & +``` - `$ docker run -dit mmquant/cpp_dev:deb9.5` - -3. Spawn bash +4. Spawn bash - `$ docker exec -it /bin/bash` - -4. Clone *bfx-cpp-api* repository into the container +```BASH +docker exec -it bfx-cpp-api_dev_1 /bin/sh +``` - `# mkdir ~/project && cd ~/project && git clone https://github.com/MMquant/bfx-cpp-api.git` - -5. Add `key-secret` file in `bfx-api-cpp/doc` directory. (or edit `example.cpp` so that it doesn't use `key-secret` file) +5. Add `key-secret` file in `/home/bfx-cpp-api/app/doc` directory. (or edit `example.cpp` so that it doesn't use `key-secret` file) - ``` - # echo > key-secret - # echo >> key-secret - ``` +```BASH +cd /home/bfx-cpp-api/app/doc +echo > key-secret +echo >> key-secret +``` -6. Build `example.cpp` +6. Build example - ``` - # mkdir ~/project/bfx-cpp-api/build - # cd ~/project/bfx-cpp-api/build - # cmake .. && make - ``` -7. Run `example` binary +```BASH +cd /home/bfx-cpp-api/app/build +cmake .. +make +``` - `$ ./example` +7. Run `example` binary +```BASH +cd /home/bfx-cpp-api/app/bin +./example +``` ### Quick interface overview - // Create API client for both authenticated and unauthenticated requests - BfxAPI::BitfinexAPI bfxAPI("accessKey", "secretKey"); - - // Create API client for just unauthenticated requests - BfxAPI::BitfinexAPI bfxAPI(); - - // Fetch data - bfxAPI.getTicker("btcusd"); - - // Check for errors - if (!bfxAPI.hasApiError()) - { - // Get response in string - cout << bfxAPI.strResponse() << endl; - } - else - { - // Inspect errors - cout << bfxAPI.getBfxApiStatusCode() << endl; - cout << bfxAPI.getCurlStatusCode() << endl; - } +```C++ +// Create API client for both authenticated and unauthenticated requests +BfxAPI::BitfinexAPI bfxAPI("accessKey", "secretKey"); + +// Create API client for just unauthenticated requests +BfxAPI::BitfinexAPI bfxAPI(); + +// Fetch data +bfxAPI.getTicker("btcusd"); + +// Check for errors +if (!bfxAPI.hasApiError()) +{ + // Get response in string + cout << bfxAPI.strResponse() << endl; +} +else +{ + // Inspect errors + cout << bfxAPI.getBfxApiStatusCode() << endl; + cout << bfxAPI.getCurlStatusCode() << endl; +} +``` See self-explanatory `src/example.cpp` for general usage and more requests. ### Change Log +- 2018-09-26 Using the small Docker image Alpine instead of Debian. +- 2018-09-26 Using docker-compose to build/up/down the image. +- 2018-09-26 Grouping project files inside the app folder. - 2018-08-01 Dockerfile added. CircleCI added. - 2018-07-24 CMakeLists.txt added. Installation instructions changed. - 2018-07-11 Schema validation logic complete. Client currently validates public requests only. @@ -123,6 +134,15 @@ See self-explanatory `src/example.cpp` for general usage and more requests. ### Known issues You will not be able to compile *bfx-cpp-api* with GCC<7.2 due to this [bug](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66297). +You can use CLANG to avoid GCC bug. + +### Contribution + +1. Fork `bfx-cpp-api` repository. +2. Commit to your ***develop*** branch. +3. Create pull request from your ***develop*** branch to ***origin/develop*** branch. + +No direct pull request to ***master*** branch accepted! ### Author diff --git a/CMakeLists.txt b/app/CMakeLists.txt similarity index 69% rename from CMakeLists.txt rename to app/CMakeLists.txt index afba25c..1a203d1 100644 --- a/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -37,3 +37,19 @@ JSON_DEFINITIONS_FILE_PATH="${PROJECT_SOURCE_DIR}/doc/definitions.json" WITHDRAWAL_CONF_FILE_PATH="${PROJECT_SOURCE_DIR}/doc/withdraw.conf") # Enable all compiler warnings target_compile_options(example PRIVATE -Wall) + +################################################################################ + +# TARGET test +add_executable (test src/test.cpp) +target_include_directories (test PRIVATE include) +target_link_libraries(test +PUBLIC bfxapicpp +PRIVATE -lcryptopp -lcurl) +# Assuming test executable built into /bin directory configuration files +# will have following paths +target_compile_definitions(test PUBLIC +JSON_DEFINITIONS_FILE_PATH="${PROJECT_SOURCE_DIR}/doc/definitions.json" +WITHDRAWAL_CONF_FILE_PATH="${PROJECT_SOURCE_DIR}/doc/withdraw.conf") +# Enable all compiler warnings +target_compile_options(test PRIVATE -Wall) diff --git a/app/build/.gitignore b/app/build/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/app/build/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/doc/definitions.json b/app/doc/definitions.json similarity index 100% rename from doc/definitions.json rename to app/doc/definitions.json diff --git a/doc/logo/bfx-cpp-api_logo.png b/app/doc/logo/bfx-cpp-api_logo.png similarity index 100% rename from doc/logo/bfx-cpp-api_logo.png rename to app/doc/logo/bfx-cpp-api_logo.png diff --git a/doc/withdraw.conf b/app/doc/withdraw.conf similarity index 100% rename from doc/withdraw.conf rename to app/doc/withdraw.conf diff --git a/include/bfx-api-cpp/BitfinexAPI.hpp b/app/include/bfx-api-cpp/BitfinexAPI.hpp similarity index 70% rename from include/bfx-api-cpp/BitfinexAPI.hpp rename to app/include/bfx-api-cpp/BitfinexAPI.hpp index 69551d7..4233539 100644 --- a/include/bfx-api-cpp/BitfinexAPI.hpp +++ b/app/include/bfx-api-cpp/BitfinexAPI.hpp @@ -35,21 +35,15 @@ #include #include -// curl -#include - -// cryptopp -#include -#include -#include -#include - // internal jsonutils #include "jsonutils.hpp" // internal error #include "error.hpp" +// internal HTTPRequest +#include "HTTPRequest.hpp" + // namespaces using std::cerr; using std::cout; @@ -59,34 +53,25 @@ using std::to_string; using std::unordered_set; using std::vector; - -// CRYPTOPP_NO_GLOBAL_BYTE signals byte is at CryptoPP::byte -#ifdef CRYPTOPP_NO_GLOBAL_BYTE -using CryptoPP::byte; -#endif - - namespace BfxAPI { - + class BitfinexAPI { - + //////////////////////////////////////////////////////////////////////// // Class constants //////////////////////////////////////////////////////////////////////// - + static constexpr auto API_URL = "https://api.bitfinex.com/v1"; - static constexpr auto CURL_TIMEOUT = 30L; - static constexpr auto CURL_DEBUG_VERBOSE = 0L; #ifndef WITHDRAWAL_CONF_FILE_PATH static constexpr auto WITHDRAWAL_CONF_FILE_PATH = "withdraw.conf"; #endif - + //////////////////////////////////////////////////////////////////////// // Typedefs //////////////////////////////////////////////////////////////////////// - + // Structure for multiple new orders endpoint struct sOrder { @@ -98,29 +83,27 @@ namespace BfxAPI }; using vOrders = vector; using vIds = vector; - + public: - + //////////////////////////////////////////////////////////////////////// // Constructor - Destructor //////////////////////////////////////////////////////////////////////// - + explicit BitfinexAPI():BitfinexAPI("", "") {} - + explicit BitfinexAPI(const string &accessKey, const string &secretKey): - accessKey_(accessKey), - secretKey_(secretKey), WDconfFilePath_(WITHDRAWAL_CONF_FILE_PATH), - APIurl_(API_URL), - curlGET_(curl_easy_init()), - curlPOST_(curl_easy_init()), - curlStatusCode_(CURLE_OK), + Request(API_URL), bfxApiStatusCode_(noError) { + // Internal HTTPRequest set Keys + Request.setAccessKey(accessKey); + Request.setSecretKey(secretKey); + // populate _symbols directly from Bitfinex getSymbols endpoint jsonutils::jsonStrToUset(symbols_, getSymbols().strResponse()); - result_.clear(); - + currencies_ = { "BTG", @@ -140,9 +123,9 @@ namespace BfxAPI "XRP", "ZEC" }; - + schemaValidator_ = jsonutils::BfxSchemaValidator(symbols_, currencies_); - + // As found on // https://bitfinex.readme.io/v1/reference#rest-auth-deposit methods_ = @@ -158,12 +141,12 @@ namespace BfxAPI "tetheruso", "zcash", }; - + walletNames_ = { "trading", "exchange", "deposit" }; - + // New order endpoint "type" parameter types_ = { @@ -179,7 +162,7 @@ namespace BfxAPI "exchange fill-or-kill" }; } - + // BitfinexAPI object cannot be // copied BitfinexAPI(const BitfinexAPI&) = delete; @@ -187,75 +170,65 @@ namespace BfxAPI // moved BitfinexAPI(BitfinexAPI&&) = delete; BitfinexAPI& operator = (BitfinexAPI&&) = delete; - - ~BitfinexAPI() - { - curl_easy_cleanup(curlGET_); - curl_easy_cleanup(curlPOST_); - } - + + ~BitfinexAPI() { } + //////////////////////////////////////////////////////////////////////// // Accessors //////////////////////////////////////////////////////////////////////// - + // Getters const string getWDconfFilePath() const noexcept { return WDconfFilePath_; } - + const BfxClientErrors& getBfxApiStatusCode() const noexcept { return bfxApiStatusCode_; } - - const CURLcode& getCurlStatusCode() const noexcept - { return curlStatusCode_; } - - const string& strResponse() const noexcept - { return result_ ; } - - bool hasApiError() const noexcept + + const CURLcode getCurlStatusCode() const noexcept + { return Request.getLastStatusCode(); } + + const string strResponse() const noexcept + { return Request.getLastResponse(); } + + bool hasApiError() { - if (bfxApiStatusCode_ == noError && curlStatusCode_ == CURLE_OK) - return false; - else - return true; + return (checkErrors() != noError || Request.hasError()); } - + // Setters - constexpr void setWDconfFilePath(const string &path) noexcept + void setWDconfFilePath(const string &path) noexcept { WDconfFilePath_ = path; } - - constexpr void setKeys(const string &accessKey, const string &secretKey) noexcept + + void setKeys(const string &accessKey, const string &secretKey) noexcept { - accessKey_ = accessKey; - secretKey_ = secretKey; + Request.setAccessKey(accessKey); + Request.setSecretKey(secretKey); } - - constexpr string& getAccessKeyRef() { return accessKey_;} - constexpr string& getSecretKeyRef() { return secretKey_;} - + //////////////////////////////////////////////////////////////////////// // Public endpoints //////////////////////////////////////////////////////////////////////// - + BitfinexAPI& getTicker(const string &symbol) { if (!inArray(symbol, symbols_)) bfxApiStatusCode_ = badSymbol; else - doGETrequest("/pubticker/" + symbol, ""); - + Request.get("/pubticker/" + symbol); + return *this; }; - + BitfinexAPI& getStats(const string &symbol) { if (!inArray(symbol, symbols_)) bfxApiStatusCode_ = badSymbol; else - doGETrequest("/stats/" + symbol, ""); - + Request.get("/stats/" + symbol); + return *this; }; - + BitfinexAPI& getFundingBook(const string ¤cy, const unsigned &limit_bids = 50, const unsigned &limit_asks = 50) @@ -264,15 +237,15 @@ namespace BfxAPI bfxApiStatusCode_ = badCurrency; else { - string params = - "?limit_bids=" + to_string(limit_bids) + - "&limit_asks=" + to_string(limit_asks); - doGETrequest("/lendbook/" + currency, params); + map params; + params["limit_bids"] = to_string(limit_bids); + params["limit_asks"] = to_string(limit_asks); + Request.get("/lendbook/" + currency, params); } - + return *this; }; - + BitfinexAPI& getOrderBook(const string &symbol, const unsigned &limit_bids = 50, const unsigned &limit_asks = 50, @@ -282,16 +255,16 @@ namespace BfxAPI bfxApiStatusCode_ = badSymbol; else { - string params = - "?limit_bids=" + to_string(limit_bids) + - "&limit_asks=" + to_string(limit_asks) + - "&group=" + to_string(group); - doGETrequest("/book/" + symbol, params); + map params; + params["limit_bids"] = to_string(limit_bids); + params["limit_asks"] = to_string(limit_asks); + params["group"] = to_string(group); + Request.get("/book/" + symbol, params); } - + return *this; }; - + BitfinexAPI& getTrades(const string &symbol, const time_t &since = 0, const unsigned &limit_trades = 50) @@ -300,15 +273,15 @@ namespace BfxAPI bfxApiStatusCode_ = badSymbol; else { - string params = - "?timestamp=" + to_string(since) + - "&limit_trades=" + to_string(limit_trades); - doGETrequest("/trades/" + symbol, params); + map params; + params["timestamp"] = to_string(since); + params["limit_trades"] = to_string(limit_trades); + Request.get("/trades/" + symbol, params); } - + return *this; }; - + BitfinexAPI& getLends(const string ¤cy, const time_t &since = 0, const unsigned &limit_lends = 50) @@ -317,115 +290,115 @@ namespace BfxAPI bfxApiStatusCode_ = badCurrency; else { - string params = - "?timestamp=" + to_string(since) + - "&limit_lends=" + to_string(limit_lends); - doGETrequest("/lends/" + currency, params); + map params; + params["timestamp"] = to_string(since); + params["limit_lends"] = to_string(limit_lends); + Request.get("/lends/" + currency, params); } - + return *this; }; - + BitfinexAPI& getSymbols() { - doGETrequest("/symbols/", ""); - + Request.get("/symbols/"); + return *this; }; - + BitfinexAPI& getSymbolsDetails() { - doGETrequest("/symbols_details/", ""); - + Request.get("/symbols_details/"); + return *this; }; - + //////////////////////////////////////////////////////////////////////// // Authenticated endpoints //////////////////////////////////////////////////////////////////////// - + // Account BitfinexAPI& getAccountInfo() { string params = "{\"request\":\"/v1/account_infos\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - doPOSTrequest("/account_infos/", params); - + Request.post("/account_infos/", params); + return *this; }; - + BitfinexAPI& getAccountFees() { string params = "{\"request\":\"/v1/account_fees\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - doPOSTrequest("/account_fees/", params); - + Request.post("/account_fees/", params); + return *this; }; - + BitfinexAPI& getSummary() { string params = "{\"request\":\"/v1/summary\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - doPOSTrequest("/summary/", params); - + Request.post("/summary/", params); + return *this; }; - + BitfinexAPI& deposit(const string &method, const string &walletName, const bool &renew = false) { if (!inArray(method, methods_)) { bfxApiStatusCode_ = badDepositMethod; return *this; } - + if (!inArray(walletName, walletNames_)) { bfxApiStatusCode_ = badWalletType; return *this; } - + string params = "{\"request\":\"/v1/deposit/new\",\"nonce\":\"" + getTonce() + "\""; params += ",\"method\":\"" + method + "\""; params += ",\"wallet_name\":\"" + walletName + "\""; params += ",\"renew\":" + to_string(renew); params += "}"; - doPOSTrequest("/deposit/new/", params); - + Request.post("/deposit/new/", params); + return *this; }; - + BitfinexAPI& getKeyPermissions() { string params = "{\"request\":\"/v1/key_info\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - doPOSTrequest("/key_info/", params); - + Request.post("/key_info/", params); + return *this; }; - + BitfinexAPI& getMarginInfos() { string params = "{\"request\":\"/v1/margin_infos\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - doPOSTrequest("/margin_infos/", params); - + Request.post("/margin_infos/", params); + return *this; }; - + BitfinexAPI& getBalances() { string params = "{\"request\":\"/v1/balances\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - doPOSTrequest("/balances/", params); - + Request.post("/balances/", params); + return *this; }; - + BitfinexAPI& transfer(const double &amount, const string ¤cy, const string &walletfrom, @@ -433,11 +406,11 @@ namespace BfxAPI { if (!inArray(currency, currencies_)) { bfxApiStatusCode_ = badCurrency; return *this; } - + if (!inArray(walletfrom, walletNames_) || !inArray(walletto, walletNames_)) { bfxApiStatusCode_ = badWalletType; return *this; } - + string params = "{\"request\":\"/v1/transfer\",\"nonce\":\"" + getTonce() + "\""; params += ",\"amount\":\"" + to_string(amount) + "\""; @@ -445,17 +418,17 @@ namespace BfxAPI params += ",\"walletfrom\":\"" + walletfrom + "\""; params += ",\"walletto\":\"" + walletto + "\""; params += "}"; - doPOSTrequest("/transfer/", params); - + Request.post("/transfer/", params); + return *this; }; - + // configure withdraw.conf file before use BitfinexAPI& withdraw() { string params = "{\"request\":\"/v1/withdraw\",\"nonce\":\"" + getTonce() + "\""; - + // Add params from withdraw.conf BfxClientErrors code(parseWDconfParams(params)); if (code != noError) @@ -463,12 +436,12 @@ namespace BfxAPI else { params += "}"; - doPOSTrequest("/withdraw/", params); + Request.post("/withdraw/", params); } - + return *this; }; - + // Orders BitfinexAPI& newOrder(const string &symbol, const double &amount, @@ -483,10 +456,10 @@ namespace BfxAPI { if (!inArray(symbol, symbols_)) { bfxApiStatusCode_ = badSymbol; return *this; }; - + if (!inArray(type, types_)) { bfxApiStatusCode_ = badOrderType; return *this; }; - + string params = "{\"request\":\"/v1/order/new\",\"nonce\":\"" + getTonce() + "\""; params += ",\"symbol\":\"" + symbol + "\""; @@ -500,20 +473,20 @@ namespace BfxAPI params += ",\"ocoorder\":" + bool2string(ocoorder); params += ",\"buy_price_oco\":" + bool2string(buy_price_oco); params += "}"; - - doPOSTrequest("/order/new/", params); + + Request.post("/order/new/", params); return *this; }; - + BitfinexAPI& newOrders(const vOrders &orders) { string params = "{\"request\":\"/v1/order/new/multi\",\"nonce\":\"" + getTonce() + "\""; - + // Get pointer to last element in orders. We will not place // ',' character at the end of the last loop. auto &last = *(--orders.cend()); - + params += ",\"payload\":["; for (const auto &order : orders) { @@ -526,31 +499,31 @@ namespace BfxAPI params += ","; } params += "]}"; - doPOSTrequest("/order/new/multi/", params); - + Request.post("/order/new/multi/", params); + return *this; }; - + BitfinexAPI& cancelOrder(const long long &order_id) { string params = "{\"request\":\"/v1/order/cancel\",\"nonce\":\"" + getTonce() + "\""; params += ",\"order_id\":" + to_string(order_id); params += "}"; - doPOSTrequest("/order/cancel/", params); - + Request.post("/order/cancel/", params); + return *this; }; - + BitfinexAPI& cancelOrders(const vIds &vOrderIds) { string params = "{\"request\":\"/v1/order/cancel/multi\",\"nonce\":\"" + getTonce() + "\""; - + // Get pointer to last element in vOrders. We will not place // ',' character at the end of the last loop. auto &last = *(--vOrderIds.cend()); - + params += ", \"order_ids\":["; for (const auto &order_id : vOrderIds) { @@ -559,21 +532,21 @@ namespace BfxAPI params += ","; } params += "]}"; - doPOSTrequest("/order/cancel/multi/", params); - + Request.post("/order/cancel/multi/", params); + return *this; }; - + BitfinexAPI& cancelAllOrders() { string params = "{\"request\":\"/v1/order/cancel/all\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - doPOSTrequest("/order/cancel/all/", params); - + Request.post("/order/cancel/all/", params); + return *this; }; - + BitfinexAPI& replaceOrder(const long long &order_id, const string &symbol, const double &amount, @@ -585,10 +558,10 @@ namespace BfxAPI { if (!inArray(symbol, symbols_)) { bfxApiStatusCode_ = badSymbol; return *this; }; - + if (!inArray(type, types_)) { bfxApiStatusCode_ = badOrderType; return *this; }; - + string params = "{\"request\":\"/v1/order/cancel/replace\",\"nonce\":\"" + getTonce() + "\""; params += ",\"order_id\":" + to_string(order_id); @@ -600,55 +573,55 @@ namespace BfxAPI params += ",\"is_hidden\":" + bool2string(is_hidden); params += ",\"use_all_available\":" + bool2string(use_remaining); params += "}"; - doPOSTrequest("/order/cancel/replace/", params); - + Request.post("/order/cancel/replace/", params); + return *this; }; - + BitfinexAPI& getOrderStatus(const long long &order_id) { string params = "{\"request\":\"/v1/order/status\",\"nonce\":\"" + getTonce() + "\""; params += ",\"order_id\":" + to_string(order_id); params += "}"; - doPOSTrequest("/order/status/", params); - + Request.post("/order/status/", params); + return *this; }; - + BitfinexAPI& getActiveOrders() { string params = "{\"request\":\"/v1/orders\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - doPOSTrequest("/orders/", params); - + Request.post("/orders/", params); + return *this; }; - + BitfinexAPI& getOrdersHistory(const unsigned &limit = 50) { string params = "{\"request\":\"/v1/orders/hist\",\"nonce\":\"" + getTonce() + "\""; params += ",\"limit\":" + to_string(limit); params += "}"; - doPOSTrequest("/orders/hist/", params); - + Request.post("/orders/hist/", params); + return *this; }; - - + + // Positions BitfinexAPI& getActivePositions() { string params = "{\"request\":\"/v1/positions\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - doPOSTrequest("/positions/", params); - + Request.post("/positions/", params); + return *this; }; - + BitfinexAPI& claimPosition(long long &position_id, const double &amount) { @@ -657,12 +630,12 @@ namespace BfxAPI params += ",\"position_id\":" + to_string(position_id); params += ",\"amount\":\"" + to_string(amount) + "\""; params += "}"; - doPOSTrequest("/position/claim/", params); - + Request.post("/position/claim/", params); + return *this; }; - - + + // Historical data BitfinexAPI& getBalanceHistory(const string ¤cy, const time_t &since = 0, @@ -673,14 +646,14 @@ namespace BfxAPI // Is currency valid ? if (!inArray(currency, currencies_)) { bfxApiStatusCode_ = badCurrency; return *this; }; - + // Is wallet type valid ? // Modified condition which accepts "all" value for all wallets // balances together.If "all" specified then there is simply no // wallet parameter in POST request. if (!inArray(walletType, walletNames_) || walletType != "all") { bfxApiStatusCode_ = badWalletType; return *this; }; - + string params = "{\"request\":\"/v1/history\",\"nonce\":\"" + getTonce() + "\""; params += ",\"currency\":\"" + currency + "\""; @@ -691,11 +664,11 @@ namespace BfxAPI if (walletType != "all") params += ",\"wallet\":\"" + walletType + "\""; params += "}"; - doPOSTrequest("/history/", params); - + Request.post("/history/", params); + return *this; }; - + BitfinexAPI& getWithdrawalHistory(const string ¤cy, const string &method = "all", const time_t &since = 0, @@ -704,10 +677,10 @@ namespace BfxAPI { if (!inArray(currency, currencies_)) { bfxApiStatusCode_ = badCurrency; return *this; }; - + if (!inArray(method, methods_) && method != "wire" && method != "all") { bfxApiStatusCode_ = badDepositMethod; return *this; }; - + string params = "{\"request\":\"/v1/history/movements\",\"nonce\":\"" + getTonce() + "\""; params += ",\"currency\":\"" + currency + "\""; @@ -718,11 +691,11 @@ namespace BfxAPI (!until ? getTonce() : to_string(until)) + "\""; params += ",\"limit\":" + to_string(limit); params += "}"; - doPOSTrequest("/history/movements/", params); - + Request.post("/history/movements/", params); + return *this; }; - + BitfinexAPI& getPastTrades(const string &symbol, const time_t ×tamp, const time_t &until = 0, @@ -742,12 +715,12 @@ namespace BfxAPI params += ",\"limit_trades\":" + to_string(limit_trades); params += ",\"reverse\":" + to_string(reverse); params += "}"; - doPOSTrequest("/mytrades/", params); + Request.post("/mytrades/", params); } - + return *this; }; - + // Margin funding BitfinexAPI& newOffer(const string ¤cy, const double &amount, @@ -767,65 +740,65 @@ namespace BfxAPI params += ",\"period\":" + to_string(period); params += ",\"direction\":\"" + direction + "\""; params += "}"; - doPOSTrequest("/offer/new/", params); + Request.post("/offer/new/", params); } - + return *this; }; - + BitfinexAPI& cancelOffer(const long long &offer_id) { string params = "{\"request\":\"/v1/offer/cancel\",\"nonce\":\"" + getTonce() + "\""; params += ",\"offer_id\":" + to_string(offer_id); params += "}"; - doPOSTrequest("/offer/cancel/", params); - + Request.post("/offer/cancel/", params); + return *this; }; - + BitfinexAPI& getOfferStatus(const long long &offer_id) { string params = "{\"request\":\"/v1/offer/status\",\"nonce\":\"" + getTonce() + "\""; params += ",\"offer_id\":" + to_string(offer_id); params += "}"; - doPOSTrequest("/offer/status/", params); - + Request.post("/offer/status/", params); + return *this; }; - + BitfinexAPI& getActiveCredits() { string params = "{\"request\":\"/v1/credits\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - doPOSTrequest("/credits/", params); - + Request.post("/credits/", params); + return *this; }; - + BitfinexAPI& getOffers() { string params = "{\"request\":\"/v1/offers\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - doPOSTrequest("/offers/", params); - + Request.post("/offers/", params); + return *this; }; - + BitfinexAPI& getOffersHistory(const unsigned &limit) { string params = "{\"request\":\"/v1/offers/hist\",\"nonce\":\"" + getTonce() + "\""; params += ",\"limit\":" + to_string(limit); params += "}"; - doPOSTrequest("/offers/hist/", params); - + Request.post("/offers/hist/", params); + return *this; }; - + // There is ambiguity in the "symbol" parameter value for this call. // It should be "currency" not "symbol". // Typical values for "symbol" are trading pairs such as "btcusd", @@ -847,70 +820,70 @@ namespace BfxAPI params += ",\"until\":" + to_string(until); params += ",\"limit_trades\":" + to_string(limit_trades); params += "}"; - doPOSTrequest("/mytrades_funding/", params); + Request.post("/mytrades_funding/", params); } - + return *this; }; - + BitfinexAPI& getTakenFunds() { string params = "{\"request\":\"/v1/taken_funds\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - doPOSTrequest("/taken_funds/", params); - + Request.post("/taken_funds/", params); + return *this; }; - + BitfinexAPI& getUnusedTakenFunds() { string params = "{\"request\":\"/v1/unused_taken_funds\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - doPOSTrequest("/unused_taken_funds/", params); - + Request.post("/unused_taken_funds/", params); + return *this; }; - + BitfinexAPI& getTotalTakenFunds() { string params = "{\"request\":\"/v1/total_taken_funds\",\"nonce\":\"" + getTonce() + "\""; params += "}"; - doPOSTrequest("/total_taken_funds/", params); - + Request.post("/total_taken_funds/", params); + return *this; }; - + BitfinexAPI& closeLoan(const long long &offer_id) { string params = "{\"request\":\"/v1/funding/close\",\"nonce\":\"" + getTonce() + "\""; params += ",\"swap_id\":" + to_string(offer_id); params += "}"; - doPOSTrequest("/funding/close/", params); - + Request.post("/funding/close/", params); + return *this; }; - + BitfinexAPI& closePosition(const long long &position_id) { string params = "{\"request\":\"/v1/position/close\",\"nonce\":\"" + getTonce() + "\""; params += ",\"position_id\":" + to_string(position_id); params += "}"; - doPOSTrequest("/position/close/", params); - + Request.post("/position/close/", params); + return *this; }; - + private: - + //////////////////////////////////////////////////////////////////////// // Private attributes //////////////////////////////////////////////////////////////////////// - + // containers with supported parameters unordered_set symbols_; // valid symbol pairs unordered_set currencies_; // valid currencies @@ -918,23 +891,18 @@ namespace BfxAPI unordered_set walletNames_; // valid walletTypes unordered_set types_; // valid Types (see new order endpoint) // BitfinexAPI settings - string accessKey_, secretKey_; string WDconfFilePath_; - string APIurl_; - // CURL instances - CURL *curlGET_; - CURL *curlPOST_; - CURLcode curlStatusCode_; // internal jsonutils instances jsonutils::BfxSchemaValidator schemaValidator_; + // internal HTTPRequest instance + HTTPRequest Request; // dynamic and status variables BfxClientErrors bfxApiStatusCode_; - string result_; - + //////////////////////////////////////////////////////////////////////// // Utility private methods //////////////////////////////////////////////////////////////////////// - + BfxClientErrors parseWDconfParams(string ¶ms) { using std::getline; @@ -943,7 +911,7 @@ namespace BfxAPI using std::regex; using std::regex_search; using std::smatch; - + string line; map mParams; ifstream inFile(WDconfFilePath_, ifstream::in); @@ -953,7 +921,7 @@ namespace BfxAPI } regex rgx("^(.*)\\b\\s*=\\s*(\"{0,1}.*\"{0,1})$"); smatch match; - + // Create map with parameters while (getline(inFile, line)) { @@ -965,7 +933,7 @@ namespace BfxAPI mParams.emplace(match[1], match[2]); } } - + // Check parameters if (!mParams.count("withdraw_type") || !mParams.count("walletselected") || @@ -973,7 +941,7 @@ namespace BfxAPI { return requiredParamsMissing; } - + if (mParams["withdraw_type"] == "wire") { if (!mParams.count("account_number") || @@ -992,9 +960,9 @@ namespace BfxAPI return addressParamsMissing; } } - + // Create JSON string - + for (const auto ¶m : mParams) { params += ",\""; @@ -1002,180 +970,39 @@ namespace BfxAPI params += "\":"; params += param.second; } - + return noError; }; - - void doGETrequest(string &&apiEndPoint, const string ¶ms) - { - bfxApiStatusCode_ = noError; - - if(curlGET_) - { - string url = APIurl_ + apiEndPoint + params; - - result_.clear(); - curl_easy_setopt(curlGET_, CURLOPT_TIMEOUT, CURL_TIMEOUT); - curl_easy_setopt(curlGET_, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curlGET_, CURLOPT_VERBOSE, CURL_DEBUG_VERBOSE); - curl_easy_setopt(curlGET_, CURLOPT_WRITEDATA, &result_); - curl_easy_setopt(curlGET_, CURLOPT_WRITEFUNCTION, WriteCallback); - - curlStatusCode_ = curl_easy_perform(curlGET_); - - // libcurl internal error handling - if (curlStatusCode_ != CURLE_OK) - { - cerr << "libcurl error in doGETrequest():\n"; - cerr << "CURLcode: " << curlStatusCode_ << "\n"; - bfxApiStatusCode_ = curlERR; - } - // Check schema - else - { - bfxApiStatusCode_ = - schemaValidator_.validateSchema(apiEndPoint, result_); - } - } - else - { - cerr << "curl not properly initialized curlGET_ = nullptr"; - bfxApiStatusCode_ = curlERR; - } - }; - - void doPOSTrequest(string &&apiEndPoint, const string ¶ms) - { - bfxApiStatusCode_ = noError; - - if(curlPOST_) - { - string url = APIurl_ + apiEndPoint; - string payload; - string signature; - getBase64(params, payload); - getHmacSha384(secretKey_, payload, signature); - - // Headers - struct curl_slist *httpHeaders = nullptr; - httpHeaders = - curl_slist_append(httpHeaders, - ("X-BFX-APIKEY:" + accessKey_).c_str()); - httpHeaders = - curl_slist_append(httpHeaders, - ("X-BFX-PAYLOAD:" + payload).c_str()); - httpHeaders = - curl_slist_append(httpHeaders, - ("X-BFX-SIGNATURE:" + signature).c_str()); - - result_.clear(); - curl_easy_setopt(curlPOST_, CURLOPT_HTTPHEADER, httpHeaders); - curl_easy_setopt(curlPOST_, CURLOPT_POST, 1); - curl_easy_setopt(curlPOST_, CURLOPT_POSTFIELDS, "\n"); - curl_easy_setopt(curlPOST_, CURLOPT_TIMEOUT, CURL_TIMEOUT); - curl_easy_setopt(curlPOST_, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curlPOST_, CURLOPT_VERBOSE, CURL_DEBUG_VERBOSE); - curl_easy_setopt(curlPOST_, CURLOPT_WRITEDATA, &result_); - curl_easy_setopt(curlPOST_, CURLOPT_WRITEFUNCTION, WriteCallback); - - curlStatusCode_ = curl_easy_perform(curlPOST_); - - // libcurl internal error handling - if (curlStatusCode_ != CURLE_OK) - { - cerr << "Libcurl error in doPOSTrequest():\n"; - cerr << "CURLcode: " << curlStatusCode_ << "\n"; - bfxApiStatusCode_ = curlERR; - } - // Check schema - else - { - bfxApiStatusCode_ = - schemaValidator_.validateSchema(apiEndPoint, result_); - } - - } - else - { - cerr << "curl not properly initialized curlPOST_ = nullptr"; - bfxApiStatusCode_ = curlERR; - } - }; - + + BfxClientErrors checkErrors() { + bfxApiStatusCode_ = Request.hasError() + ? curlERR + : schemaValidator_.validateSchema( + Request.getLastPath(), + Request.getLastResponse() + ); + return bfxApiStatusCode_; + } + //////////////////////////////////////////////////////////////////////// // Utility private static methods //////////////////////////////////////////////////////////////////////// - + const static string bool2string(const bool &in) noexcept { return in ? "true" : "false"; }; - + static string getTonce() noexcept { using namespace std::chrono; - + milliseconds ms = duration_cast(system_clock::now().time_since_epoch()); - + return to_string(ms.count()); }; - - static void getBase64(const string &content, string &encoded) - { - using CryptoPP::Base64Encoder; - using CryptoPP::StringSink; - using CryptoPP::StringSource; - - byte buffer[1024] = {}; - - for (int i = 0; i < content.length(); ++i) - buffer[i] = content[i]; - - StringSource ss(buffer, - content.length(), - true, - new Base64Encoder(new StringSink(encoded), false)); - }; - - static void getHmacSha384(const string &key, - const string &content, - string &digest) - { - using CryptoPP::HashFilter; - using CryptoPP::HexEncoder; - using CryptoPP::HMAC; - using CryptoPP::SecByteBlock; - using CryptoPP::StringSink; - using CryptoPP::StringSource; - using CryptoPP::SHA384; - using std::transform; - - SecByteBlock byteKey((const byte*)key.data(), key.size()); - string mac; - digest.clear(); - - HMAC hmac(byteKey, byteKey.size()); - StringSource ss1(content, true, - new HashFilter(hmac, new StringSink(mac))); - StringSource ss2(mac, true, new HexEncoder(new StringSink(digest))); - transform(digest.cbegin(), digest.cend(), digest.begin(), ::tolower); - }; - - // Curl write callback function. Appends fetched *content to *userp - // pointer. *userp pointer is set up by - // curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result) line. - // In this case *userp points to result. - static size_t WriteCallback(void *response, - size_t size, - size_t nmemb, - void *userp) noexcept - { - (static_cast(userp))-> - append(static_cast(response), size * nmemb); - return size * nmemb; - }; - + static bool inArray(const string &value, const unordered_set &inputSet) noexcept - { return (inputSet.find(value) != inputSet.cend()) ? true : false; }; + { return (inputSet.find(value) != inputSet.cend()); }; }; } diff --git a/app/include/bfx-api-cpp/HTTPRequest.hpp b/app/include/bfx-api-cpp/HTTPRequest.hpp new file mode 100644 index 0000000..1217784 --- /dev/null +++ b/app/include/bfx-api-cpp/HTTPRequest.hpp @@ -0,0 +1,287 @@ +//////////////////////////////////////////////////////////////////////////////// +// HTTPRequest.hpp +// +// +// Bitfinex REST API C++ client +// +// +// Copyright (C) 2018 Blas Vicco blasvicco@gmail.com +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include + +// curl +#include + +// cryptopp +#include +#include +#include +#include + +using std::string; +using std::map; + +// CRYPTOPP_NO_GLOBAL_BYTE signals byte is at CryptoPP::byte +#ifdef CRYPTOPP_NO_GLOBAL_BYTE +using CryptoPP::byte; +#endif + +namespace BfxAPI { + + class HTTPRequest { + + //////////////////////////////////////////////////////////////////////// + // Class constants + //////////////////////////////////////////////////////////////////////// + + static constexpr auto CURL_TIMEOUT = 30L; + static constexpr auto CURL_DEBUG_VERBOSE = 0L; + + public: + + //////////////////////////////////////////////////////////////////////// + // Constructor / Destructor + //////////////////////////////////////////////////////////////////////// + + HTTPRequest(string inEndpoint) { + endpoint = inEndpoint; + curlGET = curl_easy_init(); + curlPOST = curl_easy_init(); + }; + + ~HTTPRequest() { + curl_easy_cleanup(curlGET); + curl_easy_cleanup(curlPOST); + }; + + //////////////////////////////////////////////////////////////////////// + // Public methods + //////////////////////////////////////////////////////////////////////// + + string get(string inPath, map params = {}) { + response = ""; + if (curlGET) { + path = inPath; + string url = endpoint + path + "?" + parseParams(params); + + setupHeader(); + curl_easy_setopt(curlPOST, CURLOPT_HTTPHEADER, curlHeader); + curl_easy_setopt(curlGET, CURLOPT_TIMEOUT, CURL_TIMEOUT); + curl_easy_setopt(curlGET, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curlGET, CURLOPT_VERBOSE, CURL_DEBUG_VERBOSE); + curl_easy_setopt(curlGET, CURLOPT_WRITEDATA, &response); + curl_easy_setopt(curlGET, CURLOPT_WRITEFUNCTION, writeCallback); + + curlStatusCode = curl_easy_perform(curlGET); + // libcurl internal error handling + if (curlStatusCode != CURLE_OK) { + cerr << "libcurl error in Request.get():" << endl; + cerr << "CURLcode: " << curlStatusCode << endl; + } + } else { + cerr << "curl not properly initialized curlGET = nullptr"; + } + return response; + }; + + string post(string inPath, string json = "") { + response = ""; + if (curlPOST) { + path = inPath; + string url = endpoint + path; + string payload; + getBase64(json, payload); + + if (accessKey != "") { + header["X-BFX-APIKEY"] = accessKey; + } + + if (secretKey != "") { + header["X-BFX-SIGNATURE"] = getSignature(payload); + } + + if (payload != "") { + header["X-BFX-PAYLOAD"] = payload; + } + + setupHeader(); + curl_easy_setopt(curlPOST, CURLOPT_HTTPHEADER, curlHeader); + curl_easy_setopt(curlPOST, CURLOPT_POST, 1); + curl_easy_setopt(curlPOST, CURLOPT_POSTFIELDS, "\n"); + curl_easy_setopt(curlPOST, CURLOPT_TIMEOUT, CURL_TIMEOUT); + curl_easy_setopt(curlPOST, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curlPOST, CURLOPT_VERBOSE, CURL_DEBUG_VERBOSE); + curl_easy_setopt(curlPOST, CURLOPT_WRITEDATA, &response); + curl_easy_setopt(curlPOST, CURLOPT_WRITEFUNCTION, writeCallback); + + curlStatusCode = curl_easy_perform(curlPOST); + clearHeader(); + // libcurl internal error handling + if (curlStatusCode != CURLE_OK) { + cerr << "libcurl error in Request.post():" << endl; + cerr << "CURLcode: " << curlStatusCode << endl; + } + } else { + cerr << "curl not properly initialized curlPOST = nullptr"; + } + + return response; + }; + + string parseParams(map params) { + string pp = ""; + for (auto it = params.begin(); it != params.end(); it++) { + pp += it->first + "=" + it->second + "&"; + } + return pp; + }; + + string getSignature(string payload) { + string signature; + getHmacSha384(secretKey, payload, signature); + return signature; + } + + const CURLcode getLastStatusCode() const noexcept { + return curlStatusCode; + } + + const string getLastResponse() const noexcept { + return response; + } + + const string getLastPath() const noexcept { + return path; + } + + const bool hasError() const noexcept { + return curlStatusCode != CURLE_OK; + } + + void setSecretKey(string inSecretKey) { + secretKey = inSecretKey; + } + + void setAccessKey(string inAccessKey) { + accessKey = inAccessKey; + } + + void setHeader(map inHeader) { + header = inHeader; + } + + private: + + //////////////////////////////////////////////////////////////////////// + // Private properties + //////////////////////////////////////////////////////////////////////// + + string endpoint, path, secretKey, accessKey, response; + map header; + struct curl_slist *curlHeader = nullptr; + + // Curl properties + CURL *curlGET; + CURL *curlPOST; + CURLcode curlStatusCode; + + //////////////////////////////////////////////////////////////////////// + // Private methods + //////////////////////////////////////////////////////////////////////// + + // Curl write callback function. Appends fetched *content to *userp + // pointer. *userp pointer is set up by + // curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result) line. + // In this case *userp points to result. + static size_t writeCallback( + void *data, + size_t size, + size_t nmemb, + void *userp) noexcept + { + (static_cast (userp)) + ->append(static_cast (data), size * nmemb); + return size * nmemb; + }; + + static void getBase64(const string &content, string &encoded) { + using CryptoPP::Base64Encoder; + using CryptoPP::StringSink; + using CryptoPP::StringSource; + + byte buffer[1024] = {}; + + for (auto i = 0; i < content.length(); ++i) + buffer[i] = content[i]; + + StringSource ss( + buffer, + content.length(), + true, + new Base64Encoder(new StringSink(encoded), false) + ); + }; + + static void getHmacSha384( + const string &key, + const string &content, + string &digest) + { + using CryptoPP::HashFilter; + using CryptoPP::HexEncoder; + using CryptoPP::HMAC; + using CryptoPP::SecByteBlock; + using CryptoPP::StringSink; + using CryptoPP::StringSource; + using CryptoPP::SHA384; + using std::transform; + + SecByteBlock byteKey((const byte*)key.data(), key.size()); + string mac; + digest.clear(); + + HMAC hmac(byteKey, byteKey.size()); + StringSource ss1( + content, true, + new HashFilter(hmac, new StringSink(mac)) + ); + StringSource ss2(mac, true, new HexEncoder(new StringSink(digest))); + transform(digest.cbegin(), digest.cend(), digest.begin(), ::tolower); + }; + + void setupHeader() { + curlHeader = nullptr; + for (auto it = header.begin(); it != header.end(); it++) { + curlHeader = curl_slist_append( + curlHeader, + (it->first + ": " + it->second).c_str() + ); + } + }; + + void clearHeader() { + curlHeader = nullptr; + header.erase("X-BFX-APIKEY"); + header.erase("X-BFX-SIGNATURE"); + header.erase("X-BFX-PAYLOAD"); + } + + }; + +} diff --git a/include/bfx-api-cpp/error.hpp b/app/include/bfx-api-cpp/error.hpp similarity index 100% rename from include/bfx-api-cpp/error.hpp rename to app/include/bfx-api-cpp/error.hpp diff --git a/include/bfx-api-cpp/jsonutils.hpp b/app/include/bfx-api-cpp/jsonutils.hpp similarity index 100% rename from include/bfx-api-cpp/jsonutils.hpp rename to app/include/bfx-api-cpp/jsonutils.hpp diff --git a/include/rapidjson/allocators.h b/app/include/rapidjson/allocators.h similarity index 100% rename from include/rapidjson/allocators.h rename to app/include/rapidjson/allocators.h diff --git a/include/rapidjson/cursorstreamwrapper.h b/app/include/rapidjson/cursorstreamwrapper.h similarity index 100% rename from include/rapidjson/cursorstreamwrapper.h rename to app/include/rapidjson/cursorstreamwrapper.h diff --git a/include/rapidjson/document.h b/app/include/rapidjson/document.h similarity index 100% rename from include/rapidjson/document.h rename to app/include/rapidjson/document.h diff --git a/include/rapidjson/encodedstream.h b/app/include/rapidjson/encodedstream.h similarity index 100% rename from include/rapidjson/encodedstream.h rename to app/include/rapidjson/encodedstream.h diff --git a/include/rapidjson/encodings.h b/app/include/rapidjson/encodings.h similarity index 100% rename from include/rapidjson/encodings.h rename to app/include/rapidjson/encodings.h diff --git a/include/rapidjson/error/en.h b/app/include/rapidjson/error/en.h similarity index 100% rename from include/rapidjson/error/en.h rename to app/include/rapidjson/error/en.h diff --git a/include/rapidjson/error/error.h b/app/include/rapidjson/error/error.h similarity index 100% rename from include/rapidjson/error/error.h rename to app/include/rapidjson/error/error.h diff --git a/include/rapidjson/filereadstream.h b/app/include/rapidjson/filereadstream.h similarity index 100% rename from include/rapidjson/filereadstream.h rename to app/include/rapidjson/filereadstream.h diff --git a/include/rapidjson/filewritestream.h b/app/include/rapidjson/filewritestream.h similarity index 100% rename from include/rapidjson/filewritestream.h rename to app/include/rapidjson/filewritestream.h diff --git a/include/rapidjson/fwd.h b/app/include/rapidjson/fwd.h similarity index 100% rename from include/rapidjson/fwd.h rename to app/include/rapidjson/fwd.h diff --git a/include/rapidjson/internal/biginteger.h b/app/include/rapidjson/internal/biginteger.h similarity index 100% rename from include/rapidjson/internal/biginteger.h rename to app/include/rapidjson/internal/biginteger.h diff --git a/include/rapidjson/internal/diyfp.h b/app/include/rapidjson/internal/diyfp.h similarity index 100% rename from include/rapidjson/internal/diyfp.h rename to app/include/rapidjson/internal/diyfp.h diff --git a/include/rapidjson/internal/dtoa.h b/app/include/rapidjson/internal/dtoa.h similarity index 100% rename from include/rapidjson/internal/dtoa.h rename to app/include/rapidjson/internal/dtoa.h diff --git a/include/rapidjson/internal/ieee754.h b/app/include/rapidjson/internal/ieee754.h similarity index 100% rename from include/rapidjson/internal/ieee754.h rename to app/include/rapidjson/internal/ieee754.h diff --git a/include/rapidjson/internal/itoa.h b/app/include/rapidjson/internal/itoa.h similarity index 100% rename from include/rapidjson/internal/itoa.h rename to app/include/rapidjson/internal/itoa.h diff --git a/include/rapidjson/internal/meta.h b/app/include/rapidjson/internal/meta.h similarity index 100% rename from include/rapidjson/internal/meta.h rename to app/include/rapidjson/internal/meta.h diff --git a/include/rapidjson/internal/pow10.h b/app/include/rapidjson/internal/pow10.h similarity index 100% rename from include/rapidjson/internal/pow10.h rename to app/include/rapidjson/internal/pow10.h diff --git a/include/rapidjson/internal/regex.h b/app/include/rapidjson/internal/regex.h similarity index 100% rename from include/rapidjson/internal/regex.h rename to app/include/rapidjson/internal/regex.h diff --git a/include/rapidjson/internal/stack.h b/app/include/rapidjson/internal/stack.h similarity index 100% rename from include/rapidjson/internal/stack.h rename to app/include/rapidjson/internal/stack.h diff --git a/include/rapidjson/internal/strfunc.h b/app/include/rapidjson/internal/strfunc.h similarity index 100% rename from include/rapidjson/internal/strfunc.h rename to app/include/rapidjson/internal/strfunc.h diff --git a/include/rapidjson/internal/strtod.h b/app/include/rapidjson/internal/strtod.h similarity index 100% rename from include/rapidjson/internal/strtod.h rename to app/include/rapidjson/internal/strtod.h diff --git a/include/rapidjson/internal/swap.h b/app/include/rapidjson/internal/swap.h similarity index 100% rename from include/rapidjson/internal/swap.h rename to app/include/rapidjson/internal/swap.h diff --git a/include/rapidjson/istreamwrapper.h b/app/include/rapidjson/istreamwrapper.h similarity index 100% rename from include/rapidjson/istreamwrapper.h rename to app/include/rapidjson/istreamwrapper.h diff --git a/include/rapidjson/memorybuffer.h b/app/include/rapidjson/memorybuffer.h similarity index 100% rename from include/rapidjson/memorybuffer.h rename to app/include/rapidjson/memorybuffer.h diff --git a/include/rapidjson/memorystream.h b/app/include/rapidjson/memorystream.h similarity index 100% rename from include/rapidjson/memorystream.h rename to app/include/rapidjson/memorystream.h diff --git a/include/rapidjson/msinttypes/inttypes.h b/app/include/rapidjson/msinttypes/inttypes.h similarity index 100% rename from include/rapidjson/msinttypes/inttypes.h rename to app/include/rapidjson/msinttypes/inttypes.h diff --git a/include/rapidjson/msinttypes/stdint.h b/app/include/rapidjson/msinttypes/stdint.h similarity index 100% rename from include/rapidjson/msinttypes/stdint.h rename to app/include/rapidjson/msinttypes/stdint.h diff --git a/include/rapidjson/ostreamwrapper.h b/app/include/rapidjson/ostreamwrapper.h similarity index 100% rename from include/rapidjson/ostreamwrapper.h rename to app/include/rapidjson/ostreamwrapper.h diff --git a/include/rapidjson/pointer.h b/app/include/rapidjson/pointer.h similarity index 100% rename from include/rapidjson/pointer.h rename to app/include/rapidjson/pointer.h diff --git a/include/rapidjson/prettywriter.h b/app/include/rapidjson/prettywriter.h similarity index 100% rename from include/rapidjson/prettywriter.h rename to app/include/rapidjson/prettywriter.h diff --git a/include/rapidjson/rapidjson.h b/app/include/rapidjson/rapidjson.h similarity index 100% rename from include/rapidjson/rapidjson.h rename to app/include/rapidjson/rapidjson.h diff --git a/include/rapidjson/reader.h b/app/include/rapidjson/reader.h similarity index 100% rename from include/rapidjson/reader.h rename to app/include/rapidjson/reader.h diff --git a/include/rapidjson/schema.h b/app/include/rapidjson/schema.h similarity index 100% rename from include/rapidjson/schema.h rename to app/include/rapidjson/schema.h diff --git a/include/rapidjson/stream.h b/app/include/rapidjson/stream.h similarity index 100% rename from include/rapidjson/stream.h rename to app/include/rapidjson/stream.h diff --git a/include/rapidjson/stringbuffer.h b/app/include/rapidjson/stringbuffer.h similarity index 100% rename from include/rapidjson/stringbuffer.h rename to app/include/rapidjson/stringbuffer.h diff --git a/include/rapidjson/writer.h b/app/include/rapidjson/writer.h similarity index 100% rename from include/rapidjson/writer.h rename to app/include/rapidjson/writer.h diff --git a/src/example.cpp b/app/src/example.cpp similarity index 94% rename from src/example.cpp rename to app/src/example.cpp index 53e9679..cc18709 100644 --- a/src/example.cpp +++ b/app/src/example.cpp @@ -27,20 +27,19 @@ int main(int argc, char *argv[]) { // Create bfxAPI without API keys BfxAPI::BitfinexAPI bfxAPI; + // Create bfxAPI with API keys // BfxAPI::BitfinexAPI bfxAPI("accessKey", "secretKey"); - + // Load API keys from file ifstream ifs("../doc/key-secret", ifstream::in); - if (!ifs.is_open()) - { - cerr << "Can't open 'key-secret' file. " << endl; - return 1; + if (ifs.is_open()) { + string accessKey; getline(ifs, accessKey); + string secretKey; getline(ifs, secretKey); + ifs.close(); + bfxAPI.setKeys(accessKey, secretKey); } - getline(ifs, bfxAPI.getAccessKeyRef()); - getline(ifs, bfxAPI.getSecretKeyRef()); - ifs.close(); - + // Fetch API cout << "Request with error checking: " << endl; bfxAPI.getTicker("btcusd"); @@ -55,14 +54,14 @@ int main(int argc, char *argv[]) cerr << "CurlStatusCode: "; cerr << bfxAPI.getCurlStatusCode() << endl; } - + cout << "Request without error checking: " << endl; cout << bfxAPI.getSummary().strResponse() << endl; - + //////////////////////////////////////////////////////////////////////////// /// Available unauthenticated requests //////////////////////////////////////////////////////////////////////////// - + // bfxAPI.getTicker("btcusd"); // bfxAPI.getStats("btcusd"); // bfxAPI.getFundingBook("USD", 50, 50); @@ -71,11 +70,11 @@ int main(int argc, char *argv[]) // bfxAPI.getLends("USD", 0L, 50); // bfxAPI.getSymbols(); // bfxAPI.getSymbolsDetails(); - + //////////////////////////////////////////////////////////////////////////// /// Available authenticated requests //////////////////////////////////////////////////////////////////////////// - + /// Account /// // bfxAPI.getAccountInfo(); // bfxAPI.getAccountFees(); @@ -86,7 +85,7 @@ int main(int argc, char *argv[]) // bfxAPI.getBalances(); // bfxAPI.transfer(0.1, "BTC", "trading", "deposit"); // bfxAPI.withdraw(); // configure withdraw.conf file before use - + /// Orders /// // bfxAPI.newOrder("btcusd", // 0.01, @@ -131,16 +130,16 @@ int main(int argc, char *argv[]) // bfxAPI.getOrderStatus(12113548453LL); // bfxAPI.getActiveOrders(); // bfxAPI.getOrdersHistory(10); - + /// Positions /// // bfxAPI.getActivePositions(); // bfxAPI.claimPosition(156321412LL, 150); - + /// Historical data /// // bfxAPI.getBalanceHistory("USD", 0L, 0L, 500, "all"); // bfxAPI.getWithdrawalHistory("BTC", "all", 0L , 0L, 500); // bfxAPI.getPastTrades("btcusd", 0L, 0L, 500, false); - + /// Margin funding /// // bfxAPI.newOffer("USD", 12000, 25.2, 30, "lend"); // bfxAPI.cancelOffer(12354245628LL); @@ -154,6 +153,6 @@ int main(int argc, char *argv[]) // bfxAPI.getTotalTakenFunds(); // bfxAPI.closeLoan(1235845634LL); // bfxAPI.closePosition(1235845634LL); - + return 0; } diff --git a/app/src/test.cpp b/app/src/test.cpp new file mode 100644 index 0000000..8696928 --- /dev/null +++ b/app/src/test.cpp @@ -0,0 +1,72 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// test.cpp +// +// +// Bitfinex REST API C++ client - Available unauthenticated requests test +// +//////////////////////////////////////////////////////////////////////////////// + +// std +#include +#include + +// BitfinexAPI +#include "bfx-api-cpp/BitfinexAPI.hpp" + + +// namespaces +using std::cerr; +using std::cout; +using std::endl; +using std::ifstream; +using std::string; + +void check(BfxAPI::BitfinexAPI &bfxAPI) { + if (bfxAPI.hasApiError()) { + cout << "❌" << endl << endl; + cout << "BfxApiStatusCode: "; + cout << bfxAPI.getBfxApiStatusCode() << " - "; + cout << "CurlStatusCode: "; + cout << bfxAPI.getCurlStatusCode() << endl; + cout << "Response: " << bfxAPI.strResponse() << endl << endl; + } else { + cout << "✅" << endl << endl; + } +} + +int main(int argc, char *argv[]) { + // Create bfxAPI without API keys + BfxAPI::BitfinexAPI bfxAPI; + + //////////////////////////////////////////////////////////////////////////// + /// Available unauthenticated requests + //////////////////////////////////////////////////////////////////////////// + cout << "Starting available unauthenticated requests test" << endl << endl; + + cout << "- getTicker(\"btcusd\"): "; + bfxAPI.getTicker("btcusd"); check(bfxAPI); + + cout << "- getStats(\"btcusd\"): "; + bfxAPI.getStats("btcusd"); check(bfxAPI); + + cout << "- getFundingBook(\"USD\", 50, 50): "; + bfxAPI.getFundingBook("USD", 50, 50); check(bfxAPI); + + cout << "- getOrderBook(\"btcusd\", 50, 50, true): "; + bfxAPI.getOrderBook("btcusd", 50, 50, true); check(bfxAPI); + + cout << "- getTrades(\"btcusd\", 0L, 50): "; + bfxAPI.getTrades("btcusd", 0L, 50); check(bfxAPI); + + cout << "- getLends(\"USD\", 0L, 50)): "; + bfxAPI.getLends("USD", 0L, 50); check(bfxAPI); + + cout << "- getSymbols(): "; + bfxAPI.getSymbols(); check(bfxAPI); + + cout << "- getSymbolsDetails(): "; + bfxAPI.getSymbolsDetails(); check(bfxAPI); + + return 0; +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..63b4771 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +version: '3' +services: + dev: + build: . + volumes: + - ./app:/home/bfx-cpp-api/app + environment: + - CMAKE_MAKE_PROGRAM=/usr/bin/cmake + - CC=/usr/bin/clang + - CXX=/usr/bin/clang++ + command: ["/sbin/entrypoint.sh"] diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100755 index 0000000..1e2ef85 --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +# Indexing files +updatedb + +# Start supervisord and services +/usr/bin/supervisord --nodaemon -c /etc/supervisord.conf diff --git a/docker/etc/supervisord.conf b/docker/etc/supervisord.conf new file mode 100644 index 0000000..a0c0610 --- /dev/null +++ b/docker/etc/supervisord.conf @@ -0,0 +1,36 @@ +[unix_http_server] +file=/var/run/supervisor.sock ; (the path to the socket file) +chmod = 0700 ; +username = supervisor_user ; +password = supervisor_pass ; + +[supervisord] +logfile=/var/log/supervisord.log ; (main log file;default $CWD/supervisord.log) +logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB) +logfile_backups=10 ; (num of main logfile rotation backups;default 10) +loglevel=info ; (log level;default info; others: debug,warn,trace) +pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid) +nodaemon=false ; (start in foreground if true;default false) +minfds=1024 ; (min. avail startup file descriptors;default 1024) +minprocs=200 ; (min. avail process descriptors;default 200) +user=root ; + +; the below section must remain in the config file for RPC +; (supervisorctl/web interface) to work, additional interfaces may be +; added by defining them in separate rpcinterface: sections +[rpcinterface:supervisor] +supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface + +[supervisorctl] +serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL for a unix socket +username = supervisor_user ; +password = supervisor_pass ; + +[eventlistener:stdout] +command = supervisor_stdout +buffer_size = 100 +events = PROCESS_LOG +result_handler = supervisor_stdout:event_handler + +#[include] +#files = /etc/supervisor/conf.d/*.conf