Skip to content

Commit

Permalink
[Conversion] - Support transferable results for conversion
Browse files Browse the repository at this point in the history
  • Loading branch information
sjanel committed Mar 31, 2024
1 parent 4e8e605 commit c1641af
Show file tree
Hide file tree
Showing 12 changed files with 247 additions and 32 deletions.
10 changes: 4 additions & 6 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,14 @@ jobs:
sudo apt update
sudo apt install build-essential ninja-build libssl-dev libcurl4-gnutls-dev cmake git ca-certificates gzip -y --no-install-recommends
- name: Create Build Environment
run: cmake -E make_directory ${{github.workspace}}/build

- name: Configure CMake
working-directory: ${{github.workspace}}/build
run: cmake -DCMAKE_BUILD_TYPE=${{matrix.buildmode}} -GNinja ..
run: cmake -S . -B build -GNinja
env:
CMAKE_BUILD_TYPE: ${{matrix.buildmode}}

- name: Build
working-directory: ${{github.workspace}}/build
run: ninja
run: cmake --build .

# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ jobs:
run: brew install ninja

- name: Configure
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=${{matrix.buildmode}} -GNinja
run: cmake -S . -B build -GNinja
env:
CXX: ${{matrix.compiler}}
CMAKE_BUILD_TYPE: ${{matrix.buildmode}}

- name: Build
working-directory: ${{github.workspace}}/build
Expand Down
9 changes: 4 additions & 5 deletions .github/workflows/ubuntu-clang-tidy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,14 @@ jobs:
sudo apt install clang-tidy-${{matrix.clang-version}}
sudo ln -s /usr/bin/clang-tidy-${{matrix.clang-version}} /usr/local/bin/clang-tidy
- name: Create Build Environment
run: cmake -E make_directory ${{github.workspace}}/build

- name: Configure CMake
working-directory: ${{github.workspace}}/build
shell: bash
run: |
clang-tidy --dump-config
cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{matrix.buildmode}} -DCMAKE_CXX_COMPILER=clang++-${{matrix.clang-version}} -DCCT_ENABLE_CLANG_TIDY=ON -DCCT_ENABLE_ASAN=OFF -GNinja
cmake -S . -B build -DCCT_ENABLE_CLANG_TIDY=ON -DCCT_ENABLE_ASAN=OFF -GNinja
env:
CXX: clang++-${{matrix.clang-version}}
CMAKE_BUILD_TYPE: ${{matrix.buildmode}}

- name: Build
working-directory: ${{github.workspace}}/build
Expand Down
9 changes: 4 additions & 5 deletions .github/workflows/ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,12 @@ jobs:
sudo ./llvm.sh ${CLANG_VERSION}
if: startsWith(matrix.compiler, 'clang++')

- name: Create Build Environment
run: cmake -E make_directory ${{github.workspace}}/build

- name: Configure CMake
working-directory: ${{github.workspace}}/build
shell: bash
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{matrix.buildmode}} -DCMAKE_CXX_COMPILER=${{matrix.compiler}} -GNinja
run: cmake -S . -B build -GNinja
env:
CXX: ${{matrix.compiler}}
CMAKE_BUILD_TYPE: ${{matrix.buildmode}}

- name: Build
working-directory: ${{github.workspace}}/build
Expand Down
4 changes: 4 additions & 0 deletions src/engine/include/coincenter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ class Coincenter {
MonetaryAmountPerExchange getConversion(MonetaryAmount amount, CurrencyCode targetCurrencyCode,
ExchangeNameSpan exchangeNames);

/// Returns given amount per exchange converted into target currency code for given exchanges, when possible.
MonetaryAmountPerExchange getConversion(std::span<const MonetaryAmount> monetaryAmountPerExchangeToConvert,
CurrencyCode targetCurrencyCode, ExchangeNameSpan exchangeNames);

/// Query the conversion paths for each public exchange requested
ConversionPathPerExchange getConversionPaths(Market mk, ExchangeNameSpan exchangeNames);

Expand Down
3 changes: 3 additions & 0 deletions src/engine/include/exchangesorchestrator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ class ExchangesOrchestrator {
MonetaryAmountPerExchange getConversion(MonetaryAmount amount, CurrencyCode targetCurrencyCode,
ExchangeNameSpan exchangeNames);

MonetaryAmountPerExchange getConversion(std::span<const MonetaryAmount> monetaryAmountPerExchangeToConvert,
CurrencyCode targetCurrencyCode, ExchangeNameSpan exchangeNames);

ConversionPathPerExchange getConversionPaths(Market mk, ExchangeNameSpan exchangeNames);

CurrenciesPerExchange getCurrenciesPerExchange(ExchangeNameSpan exchangeNames);
Expand Down
4 changes: 4 additions & 0 deletions src/engine/include/queryresultprinter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <memory>
#include <optional>
#include <ostream>
#include <span>

#include "apioutputtype.hpp"
#include "cct_json.hpp"
Expand Down Expand Up @@ -82,6 +83,9 @@ class QueryResultPrinter {
void printConversion(MonetaryAmount amount, CurrencyCode targetCurrencyCode,
const MonetaryAmountPerExchange &conversionPerExchange) const;

void printConversion(std::span<const MonetaryAmount> startAmountPerExchangePos, CurrencyCode targetCurrencyCode,
const MonetaryAmountPerExchange &conversionPerExchange) const;

void printConversionPath(Market mk, const ConversionPathPerExchange &conversionPathsPerExchange) const;

void printWithdrawFees(const MonetaryAmountByCurrencySetPerExchange &withdrawFeesPerExchange, CurrencyCode cur) const;
Expand Down
56 changes: 48 additions & 8 deletions src/engine/src/coincenter.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "coincenter.hpp"

#include <algorithm>
#include <array>
#include <csignal>
#include <optional>
#include <span>
Expand All @@ -9,7 +10,9 @@

#include "balanceoptions.hpp"
#include "cct_exception.hpp"
#include "cct_invalid_argument_exception.hpp"
#include "cct_log.hpp"
#include "coincentercommand.hpp"
#include "coincentercommands.hpp"
#include "coincentercommandtype.hpp"
#include "coincenterinfo.hpp"
Expand All @@ -31,14 +34,23 @@

namespace cct {
namespace {
void FillTransferableCommandResults(const TradeResultPerExchange &tradeResultPerExchange,
TransferableCommandResultVector &transferableResults) {

void FillTradeTransferableCommandResults(const TradeResultPerExchange &tradeResultPerExchange,
TransferableCommandResultVector &transferableResults) {
for (const auto &[exchangePtr, tradeResult] : tradeResultPerExchange) {
if (tradeResult.isComplete()) {
transferableResults.emplace_back(exchangePtr->createExchangeName(), tradeResult.tradedAmounts().to);
}
}
}

void FillConversionTransferableCommandResults(const MonetaryAmountPerExchange &monetaryAmountPerExchange,
TransferableCommandResultVector &transferableResults) {
for (const auto &[exchangePtr, amount] : monetaryAmountPerExchange) {
transferableResults.emplace_back(exchangePtr->createExchangeName(), amount);
}
}

} // namespace

volatile sig_atomic_t g_signalStatus = 0;
Expand Down Expand Up @@ -91,7 +103,7 @@ int Coincenter::process(const CoincenterCommands &coincenterCommands) {
log::debug("Sleep for {} before next command", DurationToString(waitingDuration));
std::this_thread::sleep_for(waitingDuration);
}
if (nbRepeats != 1) {
if (nbRepeats != 1 && (repeatPos < 100 || repeatPos % 100 == 0)) {
if (nbRepeats == -1) {
log::info("Process request {}", repeatPos + 1);
} else {
Expand Down Expand Up @@ -127,8 +139,31 @@ TransferableCommandResultVector Coincenter::processCommand(
break;
}
case CoincenterCommandType::kConversion: {
const auto conversionPerExchange = getConversion(cmd.amount(), cmd.cur1(), cmd.exchangeNames());
_queryResultPrinter.printConversion(cmd.amount(), cmd.cur1(), conversionPerExchange);
if (cmd.amount().isDefault()) {
std::array<MonetaryAmount, kNbSupportedExchanges> startAmountsPerExchangePos;
bool oneSet = false;
for (const auto &transferableResult : previousTransferableResults) {
auto publicExchangePos = transferableResult.targetedExchange().publicExchangePos();
if (startAmountsPerExchangePos[publicExchangePos].isDefault()) {
startAmountsPerExchangePos[publicExchangePos] = transferableResult.resultedAmount();
oneSet = true;
} else {
throw invalid_argument(
"Transferable results to conversion should have at most one amount per public exchange");
}
}
if (!oneSet) {
throw invalid_argument("Missing input amount to convert from");
}

const auto conversionPerExchange = getConversion(startAmountsPerExchangePos, cmd.cur1(), cmd.exchangeNames());
_queryResultPrinter.printConversion(startAmountsPerExchangePos, cmd.cur1(), conversionPerExchange);
FillConversionTransferableCommandResults(conversionPerExchange, transferableResults);
} else {
const auto conversionPerExchange = getConversion(cmd.amount(), cmd.cur1(), cmd.exchangeNames());
_queryResultPrinter.printConversion(cmd.amount(), cmd.cur1(), conversionPerExchange);
FillConversionTransferableCommandResults(conversionPerExchange, transferableResults);
}
break;
}
case CoincenterCommandType::kConversionPath: {
Expand Down Expand Up @@ -220,13 +255,13 @@ TransferableCommandResultVector Coincenter::processCommand(
trade(startAmount, cmd.isPercentageAmount(), cmd.cur1(), exchangeNames, cmd.tradeOptions());
_queryResultPrinter.printTrades(tradeResultPerExchange, startAmount, cmd.isPercentageAmount(), cmd.cur1(),
cmd.tradeOptions());
FillTransferableCommandResults(tradeResultPerExchange, transferableResults);
FillTradeTransferableCommandResults(tradeResultPerExchange, transferableResults);
break;
}
case CoincenterCommandType::kBuy: {
const auto tradeResultPerExchange = smartBuy(cmd.amount(), cmd.exchangeNames(), cmd.tradeOptions());
_queryResultPrinter.printBuyTrades(tradeResultPerExchange, cmd.amount(), cmd.tradeOptions());
FillTransferableCommandResults(tradeResultPerExchange, transferableResults);
FillTradeTransferableCommandResults(tradeResultPerExchange, transferableResults);
break;
}
case CoincenterCommandType::kSell: {
Expand All @@ -238,7 +273,7 @@ TransferableCommandResultVector Coincenter::processCommand(
smartSell(startAmount, cmd.isPercentageAmount(), exchangeNames, cmd.tradeOptions());
_queryResultPrinter.printSellTrades(tradeResultPerExchange, cmd.amount(), cmd.isPercentageAmount(),
cmd.tradeOptions());
FillTransferableCommandResults(tradeResultPerExchange, transferableResults);
FillTradeTransferableCommandResults(tradeResultPerExchange, transferableResults);
break;
}
case CoincenterCommandType::kWithdrawApply: {
Expand Down Expand Up @@ -346,6 +381,11 @@ MonetaryAmountPerExchange Coincenter::getConversion(MonetaryAmount amount, Curre
return _exchangesOrchestrator.getConversion(amount, targetCurrencyCode, exchangeNames);
}

MonetaryAmountPerExchange Coincenter::getConversion(std::span<const MonetaryAmount> monetaryAmountPerExchangeToConvert,
CurrencyCode targetCurrencyCode, ExchangeNameSpan exchangeNames) {
return _exchangesOrchestrator.getConversion(monetaryAmountPerExchangeToConvert, targetCurrencyCode, exchangeNames);
}

ConversionPathPerExchange Coincenter::getConversionPaths(Market mk, ExchangeNameSpan exchangeNames) {
return _exchangesOrchestrator.getConversionPaths(mk, exchangeNames);
}
Expand Down
4 changes: 2 additions & 2 deletions src/engine/src/coincentercommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ void CoincenterCommands::addOption(const CoincenterCmdLineOptions &cmdLineOption
if (!cmdLineOptions.conversion.empty()) {
optionParser = StringOptionParser(cmdLineOptions.conversion);

const auto [amount, amountType] = optionParser.parseNonZeroAmount();
if (amountType != StringOptionParser::AmountType::kAbsolute) {
const auto [amount, amountType] = optionParser.parseNonZeroAmount(StringOptionParser::FieldIs::kOptional);
if (amountType == StringOptionParser::AmountType::kPercentage) {
throw invalid_argument("conversion should start with an absolute amount");
}
_commands.emplace_back(CoincenterCommandType::kConversion)
Expand Down
20 changes: 20 additions & 0 deletions src/engine/src/exchangesorchestrator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,26 @@ MonetaryAmountPerExchange ExchangesOrchestrator::getConversion(MonetaryAmount am
return convertedAmountPerExchange;
}

MonetaryAmountPerExchange ExchangesOrchestrator::getConversion(
std::span<const MonetaryAmount> monetaryAmountPerExchangeToConvert, CurrencyCode targetCurrencyCode,
ExchangeNameSpan exchangeNames) {
log::info("Query multiple conversions into {} from {}", targetCurrencyCode,
ConstructAccumulatedExchangeNames(exchangeNames));
UniquePublicSelectedExchanges selectedExchanges = _exchangeRetriever.selectOneAccount(exchangeNames);
MonetaryAmountPerExchange convertedAmountPerExchange(selectedExchanges.size());
_threadPool.parallelTransform(
selectedExchanges.begin(), selectedExchanges.end(), convertedAmountPerExchange.begin(),
[monetaryAmountPerExchangeToConvert, targetCurrencyCode](Exchange *exchange) {
const auto startAmount = monetaryAmountPerExchangeToConvert[exchange->publicExchangePos()];
const auto optConvertedAmount = startAmount.isDefault()
? std::nullopt
: exchange->apiPublic().estimatedConvert(startAmount, targetCurrencyCode);
return std::make_pair(exchange, optConvertedAmount.value_or(MonetaryAmount{}));
});

return convertedAmountPerExchange;
}

ConversionPathPerExchange ExchangesOrchestrator::getConversionPaths(Market mk, ExchangeNameSpan exchangeNames) {
log::info("Query {} conversion path from {}", mk, ConstructAccumulatedExchangeNames(exchangeNames));
UniquePublicSelectedExchanges selectedExchanges = _exchangeRetriever.selectOneAccount(exchangeNames);
Expand Down
57 changes: 57 additions & 0 deletions src/engine/src/queryresultprinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,37 @@ json ConversionJson(MonetaryAmount amount, CurrencyCode targetCurrencyCode,
return ToJson(CoincenterCommandType::kConversion, std::move(in), std::move(out));
}

json ConversionJson(std::span<const MonetaryAmount> startAmountPerExchangePos, CurrencyCode targetCurrencyCode,
const MonetaryAmountPerExchange &conversionPerExchange) {
json in;
json inOpt;

json fromAmounts;

int publicExchangePos{};
for (MonetaryAmount startAmount : startAmountPerExchangePos) {
if (!startAmount.isDefault()) {
fromAmounts.emplace(kSupportedExchanges[publicExchangePos], startAmount.str());
}
++publicExchangePos;
}
inOpt.emplace("sourceAmount", std::move(fromAmounts));

inOpt.emplace("targetCurrency", targetCurrencyCode.str());
in.emplace("opt", std::move(inOpt));

json out = json::object();
for (const auto &[e, convertedAmount] : conversionPerExchange) {
if (convertedAmount != 0) {
json conversionForExchange;
conversionForExchange.emplace("convertedAmount", convertedAmount.str());
out.emplace(e->name(), std::move(conversionForExchange));
}
}

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

json ConversionPathJson(Market mk, const ConversionPathPerExchange &conversionPathsPerExchange) {
json in;
json inOpt;
Expand Down Expand Up @@ -1132,6 +1163,32 @@ void QueryResultPrinter::printConversion(MonetaryAmount amount, CurrencyCode tar
logActivity(CoincenterCommandType::kConversion, jsonData);
}

void QueryResultPrinter::printConversion(std::span<const MonetaryAmount> startAmountPerExchangePos,
CurrencyCode targetCurrencyCode,
const MonetaryAmountPerExchange &conversionPerExchange) const {
json jsonData = ConversionJson(startAmountPerExchangePos, targetCurrencyCode, conversionPerExchange);
switch (_apiOutputType) {
case ApiOutputType::kFormattedTable: {
SimpleTable table;
table.reserve(1U + conversionPerExchange.size());
table.emplace_back("Exchange", "From", "To");
for (const auto &[e, convertedAmount] : conversionPerExchange) {
if (convertedAmount != 0) {
table.emplace_back(e->name(), startAmountPerExchangePos[e->publicExchangePos()].str(), convertedAmount.str());
}
}
printTable(table);
break;
}
case ApiOutputType::kJson:
printJson(jsonData);
break;
case ApiOutputType::kNoPrint:
break;
}
logActivity(CoincenterCommandType::kConversion, jsonData);
}

void QueryResultPrinter::printConversionPath(Market mk,
const ConversionPathPerExchange &conversionPathsPerExchange) const {
json jsonData = ConversionPathJson(mk, conversionPathsPerExchange);
Expand Down
Loading

0 comments on commit c1641af

Please sign in to comment.