Skip to content

Commit

Permalink
New CLI option - currencies, to get all tradable currencies
Browse files Browse the repository at this point in the history
  • Loading branch information
sjanel committed Nov 19, 2023
1 parent 7f2bcbd commit 8ccf0fb
Show file tree
Hide file tree
Showing 18 changed files with 193 additions and 12 deletions.
3 changes: 1 addition & 2 deletions data/static/currencyacronymtranslator.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
"XLTC": "LTC",
"XXLM": "XLM",
"XMLN": "MLN",
"XXDG": "XDG",
"DOGE": "XDG",
"XXDG": "DOGE",
"XZEC": "ZEC",
"ZEUR": "EUR",
"ZCAD": "CAD",
Expand Down
3 changes: 3 additions & 0 deletions src/engine/include/coincenter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ class Coincenter {

ExchangeHealthCheckStatus healthCheck(ExchangeNameSpan exchangeNames);

/// Retrieve all tradable currencies for given selected public exchanges, or all if empty.
CurrenciesPerExchange getCurrenciesPerExchange(ExchangeNameSpan exchangeNames);

/// Retrieve the markets for given selected public exchanges, or all if empty.
MarketsPerExchange getMarketsPerExchange(CurrencyCode cur1, CurrencyCode cur2, ExchangeNameSpan exchangeNames);

Expand Down
1 change: 1 addition & 0 deletions src/engine/include/coincenteroptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class CoincenterCmdLineOptions {
std::string_view monitoringUsername;
std::string_view monitoringPassword;

std::optional<std::string_view> currencies;
std::string_view markets;

std::string_view orderbook;
Expand Down
6 changes: 6 additions & 0 deletions src/engine/include/coincenteroptionsdef.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,12 @@ struct CoincenterAllowedOptions : private CoincenterCmdLineOptionsDefinitions {
"<[exch1,...]>",
"Simple health check for all exchanges or specified ones"},
&OptValueType::healthCheck},
{{{"Public queries", 2100},
"currencies",
"<[exch1,...]>",
"Print tradable currencies for all exchanges, "
"or only the specified ones."},
&OptValueType::currencies},
{{{"Public queries", 2100},
"markets",
"<cur1[-cur2][,exch1,...]>",
Expand Down
2 changes: 2 additions & 0 deletions src/engine/include/exchangesorchestrator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class ExchangesOrchestrator {

ConversionPathPerExchange getConversionPaths(Market mk, ExchangeNameSpan exchangeNames);

CurrenciesPerExchange getCurrenciesPerExchange(ExchangeNameSpan exchangeNames);

MarketsPerExchange getMarketsPerExchange(CurrencyCode cur1, CurrencyCode cur2, ExchangeNameSpan exchangeNames);

UniquePublicSelectedExchanges getExchangesTradingCurrency(CurrencyCode currencyCode, ExchangeNameSpan exchangeNames,
Expand Down
2 changes: 2 additions & 0 deletions src/engine/include/queryresultprinter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class QueryResultPrinter {

void printHealthCheck(const ExchangeHealthCheckStatus &healthCheckPerExchange) const;

void printCurrencies(const CurrenciesPerExchange &currenciesPerExchange) const;

void printMarkets(CurrencyCode cur1, CurrencyCode cur2, const MarketsPerExchange &marketsPerExchange) const;

void printMarketOrderBooks(Market mk, CurrencyCode equiCurrencyCode, std::optional<int> depth,
Expand Down
3 changes: 3 additions & 0 deletions src/engine/include/queryresulttypes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "cct_const.hpp"
#include "cct_fixedcapacityvector.hpp"
#include "cct_smallvector.hpp"
#include "currencyexchangeflatset.hpp"
#include "exchangeprivateapitypes.hpp"
#include "exchangepublicapitypes.hpp"
#include "marketorderbook.hpp"
Expand Down Expand Up @@ -43,6 +44,8 @@ using ExchangeHealthCheckStatus = FixedCapacityVector<ExchangeWith<bool>, kNbSup

using ExchangeTickerMaps = FixedCapacityVector<ExchangeWith<MarketOrderBookMap>, kNbSupportedExchanges>;

using CurrenciesPerExchange = FixedCapacityVector<ExchangeWith<CurrencyExchangeFlatSet>, kNbSupportedExchanges>;

using BalancePerExchange = SmallVector<std::pair<Exchange *, BalancePortfolio>, kTypicalNbPrivateAccounts>;

using WalletPerExchange = SmallVector<ExchangeWith<Wallet>, kTypicalNbPrivateAccounts>;
Expand Down
9 changes: 9 additions & 0 deletions src/engine/src/coincenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ TransferableCommandResultVector Coincenter::processCommand(
_queryResultPrinter.printHealthCheck(healthCheckStatus);
break;
}
case CoincenterCommandType::kCurrencies: {
const auto currenciesPerExchange = getCurrenciesPerExchange(cmd.exchangeNames());
_queryResultPrinter.printCurrencies(currenciesPerExchange);
break;
}
case CoincenterCommandType::kMarkets: {
const auto marketsPerExchange = getMarketsPerExchange(cmd.cur1(), cmd.cur2(), cmd.exchangeNames());
_queryResultPrinter.printMarkets(cmd.cur1(), cmd.cur2(), marketsPerExchange);
Expand Down Expand Up @@ -292,6 +297,10 @@ ConversionPathPerExchange Coincenter::getConversionPaths(Market mk, ExchangeName
return _exchangesOrchestrator.getConversionPaths(mk, exchangeNames);
}

CurrenciesPerExchange Coincenter::getCurrenciesPerExchange(ExchangeNameSpan exchangeNames) {
return _exchangesOrchestrator.getCurrenciesPerExchange(exchangeNames);
}

MarketsPerExchange Coincenter::getMarketsPerExchange(CurrencyCode cur1, CurrencyCode cur2,
ExchangeNameSpan exchangeNames) {
return _exchangesOrchestrator.getMarketsPerExchange(cur1, cur2, exchangeNames);
Expand Down
2 changes: 2 additions & 0 deletions src/engine/src/coincentercommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ bool CoincenterCommand::isPublic() const {
switch (_type) {
case CoincenterCommandType::kHealthCheck: // NOLINT(bugprone-branch-clone)
[[fallthrough]];
case CoincenterCommandType::kCurrencies:
[[fallthrough]];
case CoincenterCommandType::kMarkets:
[[fallthrough]];
case CoincenterCommandType::kConversionPath:
Expand Down
5 changes: 5 additions & 0 deletions src/engine/src/coincentercommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ void CoincenterCommands::addOption(const CoincenterCmdLineOptions &cmdLineOption
_commands.emplace_back(CoincenterCommandType::kHealthCheck).setExchangeNames(optionParser.parseExchanges());
}

if (cmdLineOptions.currencies) {
optionParser = StringOptionParser(*cmdLineOptions.currencies);
_commands.emplace_back(CoincenterCommandType::kCurrencies).setExchangeNames(optionParser.parseExchanges());
}

if (!cmdLineOptions.markets.empty()) {
optionParser = StringOptionParser(cmdLineOptions.markets);
_commands.push_back(CoincenterCommandFactory::CreateMarketCommand(optionParser));
Expand Down
13 changes: 13 additions & 0 deletions src/engine/src/exchangesorchestrator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,19 @@ ConversionPathPerExchange ExchangesOrchestrator::getConversionPaths(Market mk, E
return conversionPathPerExchange;
}

CurrenciesPerExchange ExchangesOrchestrator::getCurrenciesPerExchange(ExchangeNameSpan exchangeNames) {
log::info("Get all tradable currencies for {}", ConstructAccumulatedExchangeNames(exchangeNames));

UniquePublicSelectedExchanges selectedExchanges = _exchangeRetriever.selectOneAccount(exchangeNames);

CurrenciesPerExchange ret(selectedExchanges.size());
_threadPool.parallelTransform(
selectedExchanges.begin(), selectedExchanges.end(), ret.begin(),
[](Exchange *exchange) { return std::make_pair(exchange, exchange->queryTradableCurrencies()); });

return ret;
}

MarketsPerExchange ExchangesOrchestrator::getMarketsPerExchange(CurrencyCode cur1, CurrencyCode cur2,
ExchangeNameSpan exchangeNames) {
string curStr = cur1.str();
Expand Down
119 changes: 119 additions & 0 deletions src/engine/src/queryresultprinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "cct_string.hpp"
#include "coincentercommandtype.hpp"
#include "currencycode.hpp"
#include "currencycodevector.hpp"
#include "deposit.hpp"
#include "depositsconstraints.hpp"
#include "durationstring.hpp"
Expand Down Expand Up @@ -68,6 +69,35 @@ json HealthCheckJson(const ExchangeHealthCheckStatus &healthCheckPerExchange) {
return ToJson(CoincenterCommandType::kHealthCheck, std::move(in), std::move(out));
}

json CurrenciesJson(const CurrenciesPerExchange &currenciesPerExchange) {
json in;
json inOpt;
in.emplace("opt", std::move(inOpt));

json out = json::object();
for (const auto &[e, currencies] : currenciesPerExchange) {
json currenciesForExchange;
for (const CurrencyExchange &cur : currencies) {
json curExchangeJson;

curExchangeJson.emplace("code", cur.standardCode().str());
curExchangeJson.emplace("exchangeCode", cur.exchangeCode().str());
curExchangeJson.emplace("altCode", cur.altCode().str());

curExchangeJson.emplace("canDeposit", cur.canDeposit());
curExchangeJson.emplace("canWithdraw", cur.canWithdraw());
curExchangeJson.emplace("canWithdraw", cur.canWithdraw());

curExchangeJson.emplace("isFiat", cur.isFiat());

currenciesForExchange.emplace_back(std::move(curExchangeJson));
}
out.emplace(e->name(), std::move(currenciesForExchange));
}

return ToJson(CoincenterCommandType::kCurrencies, std::move(in), std::move(out));
}

json MarketsJson(CurrencyCode cur1, CurrencyCode cur2, const MarketsPerExchange &marketsPerExchange) {
json in;
json inOpt;
Expand Down Expand Up @@ -640,6 +670,95 @@ void QueryResultPrinter::printHealthCheck(const ExchangeHealthCheckStatus &healt
logActivity(CoincenterCommandType::kHealthCheck, jsonData);
}

void QueryResultPrinter::printCurrencies(const CurrenciesPerExchange &currenciesPerExchange) const {
json jsonData = CurrenciesJson(currenciesPerExchange);
switch (_apiOutputType) {
case ApiOutputType::kFormattedTable: {
SimpleTable simpleTable("Currency", "Supported exchanges", "Exchange code(s)", "Alt code(s)", "Can deposit on",
"Can withdraw from", "Is fiat");
// Compute all currencies for all exchanges
CurrencyCodeVector allCurrencyCodes;

for (const auto &[_, currencies] : currenciesPerExchange) {
allCurrencyCodes.insert(allCurrencyCodes.end(), currencies.begin(), currencies.end());
}
std::ranges::sort(allCurrencyCodes);
const auto [eraseIt1, eraseIt2] = std::ranges::unique(allCurrencyCodes);
allCurrencyCodes.erase(eraseIt1, eraseIt2);

simpleTable.reserve(1U + allCurrencyCodes.size());

for (CurrencyCode cur : allCurrencyCodes) {
string supportedExchanges;
string exchangeCodes;
string altCodes;
string canDeposit;
string canWithdraw;
std::optional<bool> isFiat;
for (const auto &[exchange, currencies] : currenciesPerExchange) {
auto it = currencies.find(cur);
if (it != currencies.end()) {
// This exchange has this currency
if (!supportedExchanges.empty()) {
supportedExchanges.push_back(',');
}
supportedExchanges.append(exchange->name());
if (!isFiat) {
isFiat = it->isFiat();
} else if (*isFiat != it->isFiat()) {
log::error("Currency {} is fiat for an exchange and not fiat for another ('{}'), consider not fiat", cur,
exchange->name());
isFiat = false;
}
if (cur != it->exchangeCode()) {
if (!exchangeCodes.empty()) {
exchangeCodes.push_back(',');
}
exchangeCodes.append(it->exchangeCode().str());
exchangeCodes.push_back('[');
exchangeCodes.append(exchange->name());
exchangeCodes.push_back(']');
}
if (cur != it->altCode()) {
if (!exchangeCodes.empty()) {
exchangeCodes.push_back(',');
}
exchangeCodes.append(it->altCode().str());
exchangeCodes.push_back('[');
exchangeCodes.append(exchange->name());
exchangeCodes.push_back(']');
}
if (it->canDeposit()) {
if (!canDeposit.empty()) {
canDeposit.push_back(',');
}
canDeposit.append(exchange->name());
}
if (it->canWithdraw()) {
if (!canWithdraw.empty()) {
canWithdraw.push_back(',');
}
canWithdraw.append(exchange->name());
}
}
}

simpleTable.emplace_back(cur.str(), std::move(supportedExchanges), std::move(exchangeCodes),
std::move(altCodes), std::move(canDeposit), std::move(canWithdraw), isFiat.value());
}

printTable(simpleTable);
break;
}
case ApiOutputType::kJson:
printJson(jsonData);
break;
case ApiOutputType::kNoPrint:
break;
}
logActivity(CoincenterCommandType::kCurrencies, jsonData);
}

void QueryResultPrinter::printMarkets(CurrencyCode cur1, CurrencyCode cur2,
const MarketsPerExchange &marketsPerExchange) const {
json jsonData = MarketsJson(cur1, cur2, marketsPerExchange);
Expand Down
1 change: 1 addition & 0 deletions src/objects/include/coincentercommandtype.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
namespace cct {
enum class CoincenterCommandType : int8_t {
kHealthCheck,
kCurrencies,
kMarkets,
kConversionPath,
kLastPrice,
Expand Down
5 changes: 2 additions & 3 deletions src/objects/include/currencyexchangeflatset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,8 @@ class CurrencyExchangeFlatSet {

const_iterator find(CurrencyCode standardCode) const {
// This is possible as CurrencyExchanges are ordered by standard code
const_iterator lbIt =
std::lower_bound(_set.begin(), _set.end(), standardCode,
[](const CurrencyExchange &lhs, CurrencyCode c) { return lhs.standardCode() < c; });
const_iterator lbIt = std::ranges::lower_bound(
_set, standardCode, [](const CurrencyExchange &lhs, CurrencyCode c) { return lhs.standardCode() < c; });
return lbIt == end() || standardCode < lbIt->standardCode() ? end() : lbIt;
}

Expand Down
5 changes: 5 additions & 0 deletions src/objects/src/coincentercommandtype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ std::string_view CoincenterCommandTypeToString(CoincenterCommandType type) {
switch (type) {
case CoincenterCommandType::kHealthCheck:
return "HealthCheck";
case CoincenterCommandType::kCurrencies:
return "Currencies";
case CoincenterCommandType::kMarkets:
return "Markets";
case CoincenterCommandType::kConversionPath:
Expand Down Expand Up @@ -57,6 +59,9 @@ CoincenterCommandType CoincenterCommandTypeFromString(std::string_view str) {
if (str == "HealthCheck") {
return CoincenterCommandType::kHealthCheck;
}
if (str == "Currencies") {
return CoincenterCommandType::kCurrencies;
}
if (str == "Markets") {
return CoincenterCommandType::kMarkets;
}
Expand Down
15 changes: 8 additions & 7 deletions src/tech/include/simpletable.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include <compare>
#include <concepts>
#include <cstdint>
#include <ios>
#include <ostream>
Expand All @@ -26,8 +28,7 @@ class SimpleTable {
class Row;

/// Cell in a SimpleTable.
/// Can currently hold only 3 types of values: a string, a string_view or an integral type.
/// TODO: add support of 'double' if needed
/// Can currently hold only 4 types of values: a string, a string_view, a int64_t and a bool.
class Cell {
public:
using IntegralType = int64_t;
Expand All @@ -39,7 +40,7 @@ class SimpleTable {
#else
using string_type = string;
#endif
using value_type = std::variant<string_type, std::string_view, IntegralType>;
using value_type = std::variant<string_type, std::string_view, IntegralType, bool>;
using size_type = uint32_t;

explicit Cell(std::string_view v) : _data(v) {}
Expand All @@ -54,17 +55,17 @@ class SimpleTable {
explicit Cell(string_type &&v) : _data(std::move(v)) {}
#endif

explicit Cell(IntegralType v) : _data(v) {}
explicit Cell(std::integral auto v) : _data(v) {}

size_type size() const noexcept;

void swap(Cell &o) noexcept { _data.swap(o._data); }

using trivially_relocatable = is_trivially_relocatable<string_type>::type;

bool operator==(const Cell &) const = default;
bool operator==(const Cell &) const noexcept = default;

auto operator<=>(const Cell &) const = default;
auto operator<=>(const Cell &) const noexcept = default;

private:
friend class Row;
Expand Down Expand Up @@ -121,7 +122,7 @@ class SimpleTable {

bool operator==(const Row &) const = default;

auto operator<=>(const Row &) const = default;
std::strong_ordering operator<=>(const Row &) const = default;

private:
friend class SimpleTable;
Expand Down
Loading

0 comments on commit 8ccf0fb

Please sign in to comment.