Skip to content

Commit

Permalink
Basic network chain precedence configuration possible for Huobi and K…
Browse files Browse the repository at this point in the history
…ucoin
  • Loading branch information
sjanel committed Dec 20, 2024
1 parent d6944a8 commit 1408e16
Show file tree
Hide file tree
Showing 11 changed files with 88 additions and 25 deletions.
7 changes: 4 additions & 3 deletions CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,10 @@ Refer to the hardcoded default json example as a model in case of doubt.

| Module | Name | Value | Description |
| ----------- | ---------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| *asset* | **allExclude** | Array of coin acronyms (ex: `["EUR", "AUD"]`)) | Exclude coins with these acronym from `coincenter` |
| *asset* | **withdrawExclude** | Array of coin acronyms (ex: `["BTC", "ETC"]`)) | Make these coins unavailable for withdraw |
| *asset* | **preferredPaymentCurrencies** | Ordered array of coin acronyms (ex: `["USDT", "BTC"]`)) | Coins that can be used for smart buy and sell as base payment currency. They should be ordered by decreasing priority. |
| *asset* | **allExclude** | Array of coin acronyms (ex: `["EUR", "AUD"]`) | Exclude coins with these acronym from `coincenter` |
| *asset* | **withdrawExclude** | Array of coin acronyms (ex: `["BTC", "ETC"]`) | Make these coins unavailable for withdraw |
| *asset* | **preferredPaymentCurrencies** | Ordered array of coin acronyms (ex: `["USDT", "BTC"]`) | Coins that can be used for smart buy and sell as base payment currency. They should be ordered by decreasing priority. |
| *asset* | **preferredChains** | Ordered array of coin acronyms (ex: `["ERC20"]`) | When a currency has several network chains, `coincenter` will pick the first one that matches a preferred chain. If the array is empty, it will attempt to pick only the chain with the same name as the currency code, if it exists. |
| *query* | **acceptEncoding** | Comma separated list of accepted encodings (ex: `"br,gzip,deflate"`), or empty | Sets list of accepted encodings that will be passed to `curl` requests as `Accept-Encoding` header. More information [here](https://curl.se/libcurl/c/CURLOPT_ACCEPT_ENCODING.html) |
| *query* | **dustAmountsThreshold** | Unordered array of monetary amounts (ex: `["1 USDT", "0.000001 BTC"]`) | For dust sweeper option, if total balance of a currency is convertible to any of these monetary amounts and if their amount is under the threshold, it will be eligible for the selling |
| *query* | **logLevels.requestsCall** | String log level for requests call ("off", "critical", "warning", "info", etc) | Specifies the log level for this exchange requests call. It prints the full public URL and the HTTP request type (GET, POST, etc) |
Expand Down
4 changes: 2 additions & 2 deletions src/api/common/include/exchangepublicapi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,6 @@ class ExchangePublic : public CacheFileUpdatorInterface {

PermanentCurlOptions::Builder permanentCurlOptionsBuilder() const;

friend class ExchangePrivate;

ExchangeNameEnum _exchangeNameEnum;
CachedResultVault _cachedResultVault;
FiatConverter &_fiatConverter;
Expand All @@ -224,6 +222,8 @@ class ExchangePublic : public CacheFileUpdatorInterface {
std::recursive_mutex _publicRequestsMutex;

private:
friend class ExchangePrivate;

AbstractMarketDataSerializer &getMarketDataSerializer();
};
} // namespace api
Expand Down
2 changes: 1 addition & 1 deletion src/api/common/src/binance-common-api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ BinanceGlobalInfos::BinanceGlobalInfosFunc::BinanceGlobalInfosFunc(AbstractMetri
schema::binance::NetworkCoinDataVector BinanceGlobalInfos::BinanceGlobalInfosFunc::operator()() {
RequestRetry requestRetry(_curlHandle, CurlOptions(HttpRequestType::kGet));

schema::binance::NetworkCoinAll ret = requestRetry.query<schema::binance::NetworkCoinAll>(
auto ret = requestRetry.query<schema::binance::NetworkCoinAll>(
"/bapi/capital/v1/public/capital/getNetworkCoinAll", [](const auto& response) {
static constexpr std::string_view kExpectedCode = "000000";
if (response.code != kExpectedCode) {
Expand Down
2 changes: 0 additions & 2 deletions src/api/exchanges/include/huobipublicapi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,6 @@ class HuobiPublic : public ExchangePublic {

WithdrawParams getWithdrawParams(CurrencyCode cur);

static bool ShouldDiscardChain(CurrencyCode cur, const schema::huobi::V2ReferenceCurrencyDetails::Chain& chainDetail);

CurlHandle _curlHandle;
CurlHandle _healthCheckCurlHandle;
CachedResult<TradableCurrenciesFunc> _tradableCurrenciesCache;
Expand Down
50 changes: 50 additions & 0 deletions src/api/exchanges/src/currency-chain-picker.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#pragma once

#include <algorithm>
#include <functional>
#include <span>
#include <string_view>
#include <utility>

#include "currencycode.hpp"
#include "exchange-asset-config.hpp"

namespace cct::api {

template <class ChainT>
class CurrencyChainPicker {
public:
CurrencyChainPicker(const schema::ExchangeAssetConfig& assetConfig,
std::function<std::string_view(const ChainT&)> chainNameFromChain)
: _preferredChains(assetConfig.preferredChains), _chainNameFromChain(std::move(chainNameFromChain)) {}

bool shouldDiscardChain(std::span<const ChainT> allChains, CurrencyCode cur, const ChainT& chainDetail) const {
std::string_view chainName = _chainNameFromChain(chainDetail);
if (!_preferredChains.empty()) {
// Display name is actually the chain name (for instance, ERC20).
// chain is the name of the currency in this chain (for instance, shib).
for (CurrencyCode preferredChain : _preferredChains) {
const auto it = std::ranges::find_if(allChains, [this, preferredChain](const auto& chain) {
return preferredChain.iequal(_chainNameFromChain(chain));
});
if (it != allChains.end()) {
// return the unique chain that matches the first preferred one, discard all the other ones
return chainName != _chainNameFromChain(*it);
}
}

return true;
}
if (!cur.iequal(chainName)) {
log::debug("Discarding chain '{}' as not supported by {}", chainName, cur);
return true;
}
return false;
}

private:
std::span<const CurrencyCode> _preferredChains;
std::function<std::string_view(const ChainT&)> _chainNameFromChain;
};

} // namespace cct::api
26 changes: 16 additions & 10 deletions src/api/exchanges/src/huobipublicapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "curlhandle.hpp"
#include "curloptions.hpp"
#include "curlpostdata.hpp"
#include "currency-chain-picker.hpp"
#include "currencycode.hpp"
#include "currencycodeset.hpp"
#include "currencyexchange.hpp"
Expand Down Expand Up @@ -132,21 +133,22 @@ schema::huobi::V2ReferenceCurrency HuobiPublic::TradableCurrenciesFunc::operator
return PublicQuery<schema::huobi::V2ReferenceCurrency>(_curlHandle, "/v2/reference/currencies");
}

bool HuobiPublic::ShouldDiscardChain(CurrencyCode cur,
const schema::huobi::V2ReferenceCurrencyDetails::Chain& chainDetail) {
if (!cur.iequal(chainDetail.chain) && !cur.iequal(chainDetail.displayName)) {
log::debug("Discarding chain '{}' as not supported by {}", chainDetail.chain, cur);
return true;
}
return false;
CurrencyChainPicker<schema::huobi::V2ReferenceCurrencyDetails::Chain> CreateCurrencyChainPicker(
const schema::ExchangeAssetConfig& assetConfig) {
return CurrencyChainPicker<schema::huobi::V2ReferenceCurrencyDetails::Chain>(
assetConfig, [](const schema::huobi::V2ReferenceCurrencyDetails::Chain& chain) -> std::string_view {
return chain.displayName;
});
}

HuobiPublic::WithdrawParams HuobiPublic::getWithdrawParams(CurrencyCode cur) {
WithdrawParams withdrawParams;
const auto& assetConfig = _coincenterInfo.exchangeConfig(exchangeNameEnum()).asset;
const auto currencyChainPicker = CreateCurrencyChainPicker(assetConfig);
for (const auto& curDetail : _tradableCurrenciesCache.get().data) {
if (cur == CurrencyCode(_coincenterInfo.standardizeCurrencyCode(curDetail.currency))) {
for (const auto& chainDetail : curDetail.chains) {
if (ShouldDiscardChain(cur, chainDetail)) {
if (currencyChainPicker.shouldDiscardChain(curDetail.chains, cur, chainDetail)) {
continue;
}

Expand All @@ -165,6 +167,8 @@ HuobiPublic::WithdrawParams HuobiPublic::getWithdrawParams(CurrencyCode cur) {

CurrencyExchangeFlatSet HuobiPublic::queryTradableCurrencies() {
CurrencyExchangeVector currencies;
const auto& assetConfig = _coincenterInfo.exchangeConfig(exchangeNameEnum()).asset;
const auto currencyChainPicker = CreateCurrencyChainPicker(assetConfig);
for (const auto& curDetail : _tradableCurrenciesCache.get().data) {
std::string_view statusStr = curDetail.instStatus;
std::string_view curStr = curDetail.currency;
Expand All @@ -175,7 +179,7 @@ CurrencyExchangeFlatSet HuobiPublic::queryTradableCurrencies() {
bool foundChainWithSameName = false;
CurrencyCode cur(_coincenterInfo.standardizeCurrencyCode(curStr));
for (const auto& chainDetail : curDetail.chains) {
if (ShouldDiscardChain(cur, chainDetail)) {
if (currencyChainPicker.shouldDiscardChain(curDetail.chains, cur, chainDetail)) {
continue;
}
auto depositAllowed = chainDetail.depositStatus == "allowed" ? CurrencyExchange::Deposit::kAvailable
Expand Down Expand Up @@ -265,12 +269,14 @@ std::pair<MarketSet, HuobiPublic::MarketsFunc::MarketInfoMap> HuobiPublic::Marke

MonetaryAmountByCurrencySet HuobiPublic::queryWithdrawalFees() {
MonetaryAmountVector fees;
const auto& assetConfig = _coincenterInfo.exchangeConfig(exchangeNameEnum()).asset;
const auto currencyChainPicker = CreateCurrencyChainPicker(assetConfig);
for (const auto& curDetail : _tradableCurrenciesCache.get().data) {
std::string_view curStr = curDetail.currency;
CurrencyCode cur(_coincenterInfo.standardizeCurrencyCode(curStr));
bool foundChainWithSameName = false;
for (const auto& chainDetail : curDetail.chains) {
if (ShouldDiscardChain(cur, chainDetail)) {
if (currencyChainPicker.shouldDiscardChain(curDetail.chains, cur, chainDetail)) {
continue;
}
std::string_view withdrawFeeTypeStr = chainDetail.withdrawFeeType;
Expand Down
3 changes: 3 additions & 0 deletions src/api/exchanges/src/upbit-schema.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once

namespace cct::schema::upbit {}
2 changes: 1 addition & 1 deletion src/objects/include/currencyexchange.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class CurrencyExchange {

CurrencyExchange() noexcept = default;

/// Constructs a CurrencyExchange with a standard code, with unknown withdrawal / deposit statuses
/// Constructs a CurrencyExchange with a standard code, with unknown (disabled) withdrawal / deposit statuses
CurrencyExchange(CurrencyCode standardCode)
: _standardCode(standardCode), _exchangeCode(standardCode), _altCode(standardCode) {}

Expand Down
3 changes: 1 addition & 2 deletions src/objects/src/currencyexchange.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ string CurrencyExchange::str() const {
}

std::ostream &operator<<(std::ostream &os, const CurrencyExchange &currencyExchange) {
os << currencyExchange.str();
return os;
return os << currencyExchange.str();
}

} // namespace cct
10 changes: 8 additions & 2 deletions src/schema/include/exchange-asset-config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "cct_type_traits.hpp"
#include "currencycodeset.hpp"
#include "currencycodevector.hpp"

namespace cct::schema {

Expand All @@ -10,13 +11,18 @@ struct ExchangeAssetConfig {

void mergeWith(const ExchangeAssetConfig &other) {
allExclude.insert(other.allExclude.begin(), other.allExclude.end());
preferredPaymentCurrencies.insert(other.preferredPaymentCurrencies.begin(), other.preferredPaymentCurrencies.end());
preferredPaymentCurrencies.insert(preferredPaymentCurrencies.begin(), other.preferredPaymentCurrencies.begin(),
other.preferredPaymentCurrencies.end());
withdrawExclude.insert(other.withdrawExclude.begin(), other.withdrawExclude.end());
preferredChains.insert(preferredChains.begin(), other.preferredChains.begin(), other.preferredChains.end());
}

CurrencyCodeSet allExclude;
CurrencyCodeSet preferredPaymentCurrencies;
CurrencyCodeVector preferredPaymentCurrencies;
CurrencyCodeSet withdrawExclude;
// when there are several chains available for a currency, pick the first that matches this list
// Set this to ensure same chains are used between exchanges.
CurrencyCodeVector preferredChains;
};

} // namespace cct::schema
4 changes: 2 additions & 2 deletions src/schema/include/exchange-config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ class AllExchangeConfigs {

explicit AllExchangeConfigs(const LoadConfiguration &loadConfiguration);

const ExchangeConfig &operator[](ExchangeNameEnum exchangeName) const {
return _exchangeConfigs[static_cast<int>(exchangeName)];
const ExchangeConfig &operator[](ExchangeNameEnum exchangeNameEnum) const {
return _exchangeConfigs[static_cast<int>(exchangeNameEnum)];
}

void mergeWith(const details::AllExchangeConfigsOptional &other);
Expand Down

0 comments on commit 1408e16

Please sign in to comment.