mirror of https://github.com/layters/testshop
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
338 lines
15 KiB
338 lines
15 KiB
#include "currency_converter.hpp"
|
|
// copied from price_api_factory.cpp
|
|
#include "coincodex/coincodex.hpp"
|
|
#include "coingecko/coingecko.hpp"
|
|
#include "coinmarketcap/coinmarketcap.hpp"
|
|
#include "cointelegraph/cointelegraph.hpp"
|
|
#include "cryptorank/cryptorank.hpp"
|
|
#include "cryptowatch/cryptowatch.hpp"
|
|
#include "fawazahmed0/fawazahmed0.hpp"
|
|
#include "kraken/kraken.hpp"
|
|
|
|
#include "../tools/logger.hpp"
|
|
#include "../tools/string.hpp"
|
|
#include "currency_map.hpp" // neroshop::CurrencyMap
|
|
//-------------------------------------------------------
|
|
//-------------------------------------------------------
|
|
std::string neroshop::Converter::json_string ("");
|
|
//-------------------------------------------------------
|
|
//-------------------------------------------------------
|
|
double neroshop::Converter::to_kg(double amount, const std::string& unit_name) {
|
|
if(neroshop::string::lower(unit_name) == "lb" || neroshop::string::lower(unit_name) == "lbs" || neroshop::string::lower(unit_name) == "pound") {return lb_to_kg(amount);}
|
|
return 0.0;
|
|
}
|
|
//-------------------------------------------------------
|
|
double neroshop::Converter::lb_to_kg(double lb) {
|
|
double lb2kg = 0.45359237; // 1 lb = 0.45359237 kg
|
|
return lb * lb2kg;
|
|
}
|
|
//-------------------------------------------------------
|
|
//-------------------------------------------------------
|
|
std::unique_ptr<neroshop::PriceApi> neroshop::Converter::make_price_source(PriceSource source)
|
|
{
|
|
switch (source) {
|
|
// Crypto Data Aggregators
|
|
case PriceSource::CoinMarketCap:
|
|
return std::make_unique<CoinMarketCapApi>();
|
|
case PriceSource::CoinGecko:
|
|
return std::make_unique<CoinGeckoApi>();
|
|
case PriceSource::CryptoWatch:
|
|
return std::make_unique<CryptoWatchApi>();
|
|
case PriceSource::CoinTelegraph:
|
|
return std::make_unique<CoinTelegraphApi>();
|
|
case PriceSource::CryptoRank:
|
|
return std::make_unique<CryptoRankApi>();
|
|
case PriceSource::CoinCodex:
|
|
return std::make_unique<CoinCodexApi>();
|
|
case PriceSource::Fawazahmed0:
|
|
return std::make_unique<Fawazahmed0CurrencyApi>();
|
|
// Exchanges
|
|
case PriceSource::Kraken:
|
|
return std::make_unique<KrakenApi>();
|
|
}
|
|
return nullptr;
|
|
}
|
|
//-------------------------------------------------------
|
|
double neroshop::Converter::get_price(neroshop::Currency from, neroshop::Currency to) {
|
|
const std::vector<neroshop::PriceSource> SOURCES_TO_USE{
|
|
neroshop::PriceSource::CoinMarketCap,
|
|
neroshop::PriceSource::CoinGecko,
|
|
neroshop::PriceSource::CryptoWatch,
|
|
neroshop::PriceSource::CoinTelegraph,
|
|
neroshop::PriceSource::CryptoRank,
|
|
neroshop::PriceSource::CoinCodex,
|
|
neroshop::PriceSource::Fawazahmed0,
|
|
// Exchanges
|
|
neroshop::PriceSource::Kraken,
|
|
};
|
|
|
|
double price = 0.0;
|
|
for (const auto &source : SOURCES_TO_USE) {
|
|
auto price_source = make_price_source(source);
|
|
auto price_opt = price_source->price(from, to);
|
|
if(price_opt.has_value()) {
|
|
price = price_opt.value();//std::cout << "get_price result: " << price << "\n";
|
|
return price;
|
|
}
|
|
}
|
|
return 0.0;
|
|
}
|
|
//-------------------------------------------------------
|
|
double neroshop::Converter::convert_to_xmr(double amount, const std::string& currency) {
|
|
std::string map_key = neroshop::string::upper(currency);
|
|
|
|
if(neroshop::CurrencyMap.count(map_key) > 0) {
|
|
auto map_value = neroshop::CurrencyMap[map_key];
|
|
neroshop::Currency from_currency = std::get<0>(map_value);
|
|
double rate = neroshop::Converter::get_price(neroshop::Currency::XMR, from_currency); // 1 xmr = ? currency//std::cout << amount << " " << map_key << " is equal to " << neroshop::string::precision((amount / rate), 12) << " XMR\n";
|
|
return (amount / rate);
|
|
}
|
|
neroshop::print(neroshop::string::upper(currency) + " is not supported", 1);
|
|
return 0.0;
|
|
}
|
|
//-------------------------------------------------------
|
|
std::vector<std::string> neroshop::Converter::get_currency_list() {
|
|
std::vector<std::string> currency_list;
|
|
for (const auto& [key, value] : neroshop::CurrencyMap) {
|
|
currency_list.push_back(key);
|
|
}
|
|
return currency_list;
|
|
}
|
|
//-------------------------------------------------------
|
|
int neroshop::Converter::get_currency_decimals(const std::string& currency) {
|
|
auto map_key = neroshop::string::upper(currency);
|
|
// Check if key exists in std::map
|
|
if(neroshop::CurrencyMap.count(map_key) > 0) {
|
|
auto map_value = neroshop::CurrencyMap[map_key];
|
|
int decimal_places = std::get<2>(map_value);
|
|
return decimal_places;
|
|
}
|
|
return 2;
|
|
}
|
|
//-------------------------------------------------------
|
|
double neroshop::Converter::get_xmr_price(const std::string& currency) {
|
|
auto map_key = neroshop::string::upper(currency);
|
|
// Check if key exists in std::map
|
|
if(neroshop::CurrencyMap.count(map_key) > 0) {////if(neroshop::CurrencyMap.find(map_key) != neroshop::CurrencyMap.end()) {
|
|
auto map_value = neroshop::CurrencyMap[map_key];
|
|
neroshop::Currency preferred_currency = std::get<0>(map_value);
|
|
return neroshop::Converter::get_price(neroshop::Currency::XMR, preferred_currency);
|
|
}
|
|
neroshop::print(neroshop::string::upper(currency) + " is not supported", 1);
|
|
return 0.0;
|
|
}
|
|
//-------------------------------------------------------
|
|
std::string neroshop::Converter::get_currency_sign(const std::string& currency_code) {
|
|
if(neroshop::string::lower(currency_code) == "usd") return "$";// or US$
|
|
if(neroshop::string::lower(currency_code) == "eur") return "€";
|
|
if(neroshop::string::lower(currency_code) == "jpy") return "¥";
|
|
if(neroshop::string::lower(currency_code) == "gbp") return "£";
|
|
if(neroshop::string::lower(currency_code) == "cad") return "$"; // or "$", "C$", "CA$", "CAD$";
|
|
if(neroshop::string::lower(currency_code) == "chf") return "CHF"; // does not have an actual sign so CHF is used
|
|
if(neroshop::string::lower(currency_code) == "aud") return "$"; // or "$", "A$", "AUD$";
|
|
if(neroshop::string::lower(currency_code) == "cny") return "¥";//or "元"
|
|
if(neroshop::string::lower(currency_code) == "sek") return "kr";// e.g 20 kr, 50 kr, 100 kr, etc.
|
|
if(neroshop::string::lower(currency_code) == "nzd") return "$";// NZ$ or $
|
|
if(neroshop::string::lower(currency_code) == "mxn") return "$";// MX$ or $
|
|
if(neroshop::string::lower(currency_code) == "ngn") return "₦";
|
|
if(neroshop::string::lower(currency_code) == "ghs") return "₵";
|
|
if(neroshop::string::lower(currency_code) == "rub") return "₽";
|
|
if(neroshop::string::lower(currency_code) == "php") return "₱";
|
|
if(neroshop::string::lower(currency_code) == "inr") return "₹";
|
|
//if(neroshop::string::lower(currency_code) == "") return "";
|
|
return "";
|
|
} // https://www.xe.com/symbols.php
|
|
//-------------------------------------------------------
|
|
neroshop::Currency neroshop::Converter::get_currency_enum(const std::string& currency) {
|
|
auto map_key = neroshop::string::upper(currency);
|
|
if(neroshop::CurrencyMap.count(map_key) > 0) {
|
|
auto map_value = neroshop::CurrencyMap[map_key];
|
|
return std::get<0>(map_value);
|
|
}
|
|
return neroshop::Currency::USD;
|
|
}
|
|
//-------------------------------------------------------
|
|
//-------------------------------------------------------
|
|
//-------------------------------------------------------
|
|
bool neroshop::Converter::is_supported_currency(const std::string& currency_code) {
|
|
return (neroshop::CurrencyMap.count(neroshop::string::upper(currency_code)) > 0);
|
|
}
|
|
//-------------------------------------------------------
|
|
//-------------------------------------------------------
|
|
//-------------------------------------------------------
|
|
//-------------------------------------------------------
|
|
double neroshop::Converter::convert_xmr(double quantity, std::string currency, bool to) { //to: if we want currency->xmr (true) or rather xmr->currency (false)
|
|
#if !defined(NEROSHOP_USE_QT)
|
|
std::map<std::string, std::string> currency_to_id_coinmarketcap = {
|
|
{"usd", "2781"}, {"aud", "2782"}, {"cad", "2784"}, {"chf", "2785"}, {"cny", "2787"}, {"eur", "2790"},
|
|
{"gbp", "2791"}, {"jpy", "2797"}, {"mxn", "2799"}, {"nzd", "2802"}, {"sek", "2807"}, {"btc", "1"}, {"eth", "1027"},
|
|
}; //We can easily add new currencies, it must be added here in the coinmarketcap url that fetch usd prices
|
|
|
|
currency = neroshop::string::lower(currency);
|
|
|
|
//Definition of variables that will be used later
|
|
std::vector<double> prices;
|
|
double price;
|
|
nlohmann::json json_response;
|
|
std::string url;
|
|
std::string response;
|
|
std::map<std::string, double> currencies_in_usd;
|
|
|
|
url = "https://api.coinmarketcap.com/data-api/v3/cryptocurrency/quote/latest?id=2781,2782,2784,2785,2787,2790,2791,2797,2799,2802,2807,1,1027&convertId=2781";
|
|
if (request(url)) {
|
|
response = get_json();
|
|
json_response = nlohmann::json::parse(response);
|
|
auto currencies = json_response["data"];
|
|
|
|
for (int i = 0; i<currencies.size(); i++) {
|
|
std::string name = neroshop::string::lower(currencies[i]["symbol"]);
|
|
currencies_in_usd[name] = currencies[i]["quotes"][0]["price"];
|
|
}
|
|
}
|
|
json_string.clear();
|
|
|
|
if (is_supported_currency(currency) && quantity >= 0) {
|
|
|
|
//From coingecko
|
|
url = "https://api.coingecko.com/api/v3/simple/price?ids=monero&vs_currencies=" + currency;
|
|
if (request(url)) {
|
|
response = get_json();
|
|
json_response = nlohmann::json::parse(response);
|
|
price = json_response["monero"][currency];
|
|
prices.push_back(price);
|
|
}
|
|
json_string.clear();
|
|
|
|
//From coinmarketcap
|
|
url = "https://api.coinmarketcap.com/data-api/v3/cryptocurrency/quote/latest?id=328&convertId=" + currency_to_id_coinmarketcap[currency];
|
|
if (request(url)) {
|
|
response = get_json();
|
|
json_response = nlohmann::json::parse(response);
|
|
price = json_response["data"][0]["quotes"][0]["price"];
|
|
prices.push_back(price);
|
|
}
|
|
json_string.clear();
|
|
|
|
//From cryptowatch
|
|
url = "https://billboard.service.cryptowat.ch/markets?sort=price&onlyBaseAssets=xmr&onlyQuoteAssets=usd";
|
|
if (request(url)) {
|
|
response = get_json();
|
|
json_response = nlohmann::json::parse(response);
|
|
auto list_exchanges = json_response["result"]["rows"];
|
|
for (int i = 0; i<list_exchanges.size(); i++) { //Prices are in usd only so we need to convert it
|
|
price = list_exchanges[i]["lastPriceByAsset"]["usd"];
|
|
prices.push_back(price/currencies_in_usd[currency]);
|
|
}
|
|
}
|
|
json_string.clear();
|
|
|
|
//From cointelegraph
|
|
url = "https://ticker-api.cointelegraph.com/rates/?full=true";
|
|
if (request(url)) {
|
|
response = get_json();
|
|
json_response = nlohmann::json::parse(response);
|
|
if (json_response["data"]["XMR"][neroshop::string::upper(currency)] != nullptr) {
|
|
price = json_response["data"]["XMR"][neroshop::string::upper(currency)]["price"];
|
|
} else {
|
|
price = (double)json_response["data"]["XMR"]["USD"]["price"]/currencies_in_usd[currency];
|
|
}
|
|
prices.push_back(price);
|
|
}
|
|
json_string.clear();
|
|
|
|
//From cryptorank
|
|
url = "https://api.cryptorank.io/v0/coins/monero?locale=en";
|
|
if (request(url)) {
|
|
response = get_json();
|
|
json_response = nlohmann::json::parse(response);
|
|
double price_usd = json_response["data"]["price"]["USD"];
|
|
price = price_usd/currencies_in_usd[currency];
|
|
prices.push_back(price);
|
|
}
|
|
json_string.clear();
|
|
|
|
//From coincodex
|
|
url = "https://coincodex.com/api/coincodex/get_coin/xmr";
|
|
if (request(url)) {
|
|
response = get_json();
|
|
json_response = nlohmann::json::parse(response);
|
|
double price_usd = json_response["last_price_usd"];
|
|
price = price_usd/currencies_in_usd[currency];
|
|
prices.push_back(price);
|
|
}
|
|
json_string.clear();
|
|
|
|
//Final computation of the price
|
|
if (prices.size() > 0) { //Check if at least one request worked
|
|
double sum_price = 0;
|
|
for (double p : prices) {
|
|
sum_price += p;
|
|
}
|
|
price = sum_price/prices.size();
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
if (to) {
|
|
return quantity/price;
|
|
} else {
|
|
return price*quantity;
|
|
}
|
|
}
|
|
#endif
|
|
return -1;
|
|
}
|
|
//-------------------------------------------------------
|
|
//-------------------------------------------------------
|
|
//-------------------------------------------------------
|
|
//-------------------------------------------------------
|
|
bool neroshop::Converter::request(const std::string& url)
|
|
{
|
|
#if !defined(NEROSHOP_USE_QT)
|
|
// parse raw json str
|
|
//std::string buffer;
|
|
CURL * curl = curl_easy_init();
|
|
long http_code = 0;
|
|
if(curl) {
|
|
CURLcode res;
|
|
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/7.84.0-DEV"); //User aget needed for Cloudflare security from coinmarketcap
|
|
//curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); // opt - Don't bother trying IPv6, which would increase DNS resolution time.
|
|
//curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10); // opt - don't wait forever, time out after 10 secs
|
|
//curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // opt follow https redirects if necessary
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, neroshop::Converter::write_callback);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &neroshop::Converter::json_string);//&buffer);
|
|
res = curl_easy_perform(curl);
|
|
//curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); // opt
|
|
if(res != CURLE_OK) {
|
|
std::cerr << "curl_easy_perform() failed:" << curl_easy_strerror(res) << std::endl;
|
|
return false;
|
|
}
|
|
curl_easy_cleanup(curl);
|
|
//if (http_code == 200) std::cout << "\nGot successful response from " << url << std::endl; // opt
|
|
}
|
|
else { std::cout << "Could not initialize curl" << std::endl; return false; }
|
|
#endif
|
|
return true;
|
|
}
|
|
//-------------------------------------------------------
|
|
std::string neroshop::Converter::get_json() // const; // returns whole json as a string
|
|
{
|
|
return json_string;
|
|
}
|
|
//-------------------------------------------------------
|
|
//-------------------------------------------------------
|
|
std::size_t neroshop::Converter::write_callback(char* in, std::size_t size, std::size_t num, std::string* out)
|
|
{
|
|
const std::size_t total_bytes = size * num;
|
|
if(total_bytes) {
|
|
out->append(in, total_bytes);
|
|
return total_bytes;
|
|
}
|
|
return 0;
|
|
}
|
|
//-------------------------------------------------------
|
|
//-------------------------------------------------------
|
|
//-------------------------------------------------------
|
|
//-------------------------------------------------------
|