Skip to content

Commit

Permalink
Refactor and unit test coincenteroptions
Browse files Browse the repository at this point in the history
  • Loading branch information
sjanel committed Nov 3, 2023
1 parent 715d209 commit cefa95e
Show file tree
Hide file tree
Showing 24 changed files with 781 additions and 588 deletions.
2 changes: 1 addition & 1 deletion src/api-objects/include/tradeoptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class TradeOptions {

string str(bool placeRealOrderInSimulationMode) const;

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

private:
Duration _maxTradeTime = kDefaultTradeDuration;
Expand Down
9 changes: 8 additions & 1 deletion src/engine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ target_link_libraries(coincenter_engine PUBLIC coincenter_api-interface)
target_link_libraries(coincenter_engine PUBLIC coincenter_objects)
target_include_directories(coincenter_engine PUBLIC include)

add_unit_test(
coincenteroptions_test
test/coincenteroptions_test.cpp
LIBRARIES
coincenter_engine
)

add_unit_test(
commandlineoptionsparser_test
test/commandlineoptionsparser_test.cpp
Expand Down Expand Up @@ -57,4 +64,4 @@ add_unit_test(
test/stringoptionparser_test.cpp
LIBRARIES
coincenter_engine
)
)
1 change: 0 additions & 1 deletion src/engine/include/balanceperexchangeportfolio.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#pragma once

#include "cct_const.hpp"
#include "cct_json.hpp"
#include "queryresulttypes.hpp"
#include "simpletable.hpp"
Expand Down
449 changes: 29 additions & 420 deletions src/engine/include/coincenteroptions.hpp

Large diffs are not rendered by default.

421 changes: 421 additions & 0 deletions src/engine/include/coincenteroptionsdef.hpp

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions src/engine/include/commandlineoption.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ class CommandHeader {

constexpr int prio() const { return _prio; }

constexpr auto operator<=>(const CommandHeader& other) const = default;
constexpr std::strong_ordering operator<=>(const CommandHeader&) const = default;

private:
// Order of members is important because of the default spaceship operator. _prio should be first
int _prio = 0;
std::string_view _groupName;
};
Expand Down Expand Up @@ -55,7 +56,7 @@ class CommandLineOption {

constexpr bool hasShortName() const { return _shortName != '\0'; }

constexpr auto operator<=>(const CommandLineOption& other) const = default;
constexpr std::strong_ordering operator<=>(const CommandLineOption&) const = default;

private:
static constexpr std::string_view kLegacyFullNamePrefixOption = "--";
Expand Down Expand Up @@ -85,6 +86,8 @@ class CommandLineOptionalInt {
constexpr bool isPresent() const { return _state == State::kOptionPresent || _state == State::kValueIsSet; }
constexpr bool isSet() const { return _state == State::kValueIsSet; }

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

private:
int _value = 0;
State _state = State::kOptionNotPresent;
Expand Down
36 changes: 19 additions & 17 deletions src/engine/include/commandlineoptionsparser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
#include <algorithm>
#include <functional>
#include <iterator>
#include <memory>
#include <optional>
#include <ostream>
#include <span>
#include <string_view>
#include <type_traits>
#include <unordered_map>

#include "cct_cctype.hpp"
Expand Down Expand Up @@ -82,7 +82,7 @@ class CommandLineOptionsParser {
}

void displayHelp(std::string_view programName, std::ostream& stream) const {
stream << "usage: " << programName << " <general options> <command(s)>\n";
stream << "usage: " << programName << " <general options> [command(s)]\n";
if (_opts.empty()) {
return;
}
Expand Down Expand Up @@ -150,32 +150,34 @@ class CommandLineOptionsParser {
return std::ranges::none_of(_opts, [opt](const auto& cmdLineOpt) { return cmdLineOpt.first.matches(opt); });
}

static bool IsOptionPositiveInt(std::string_view opt) { return std::ranges::all_of(opt, isdigit); }
static bool AreAllDigits(std::string_view opt) { return std::ranges::all_of(opt, isdigit); }

static bool IsOptionInt(std::string_view opt) {
return ((opt[0] == '-' || opt[0] == '+') && std::all_of(std::next(opt.begin()), opt.end(), isdigit)) ||
IsOptionPositiveInt(opt);
AreAllDigits(opt);
}

CallbackType registerCallback(const CommandLineOption& commandLineOption, CommandLineOptionType prop,
OptValueType& data) {
return [this, &commandLineOption, prop, &data](int& idx, std::span<const char*> argv) {
if (commandLineOption.matches(argv[idx])) {
std::visit(overloaded{
// bool value matcher
[&data](bool OptValueType::*arg) { data.*arg = true; },

// int value matcher
[&data, &idx, argv, &commandLineOption](int OptValueType::*arg) {
if (idx + 1U < argv.size()) {
std::string_view nextOpt(argv[idx + 1]);
if (IsOptionInt(nextOpt)) {
data.*arg = FromString<int>(nextOpt);
++idx;
return;
// integral value matcher including bool
[&data, &idx, argv, &commandLineOption](std::integral auto OptValueType::*arg) {
using IntType = std::remove_reference_t<decltype(data.*arg)>;
if constexpr (std::is_same_v<IntType, bool>) {
data.*arg = true;
} else {
if (idx + 1U < argv.size()) {
std::string_view nextOpt(argv[idx + 1]);
if (IsOptionInt(nextOpt)) {
data.*arg = FromString<IntType>(nextOpt);
++idx;
return;
}
}
ThrowExpectingValueException(commandLineOption);
}
ThrowExpectingValueException(commandLineOption);
},

// CommandLineOptionalInt value matcher
Expand Down Expand Up @@ -262,7 +264,7 @@ class CommandLineOptionsParser {
LevenshteinDistanceCalculator calc;
std::ranges::transform(_opts, minDistancesToFullNameOptions.begin(),
[argStr, &calc](const auto opt) { return calc(opt.first.fullName(), argStr); });
auto optIt = std::ranges::min_element(minDistancesToFullNameOptions);
const auto optIt = std::ranges::min_element(minDistancesToFullNameOptions);
return {optIt - minDistancesToFullNameOptions.begin(), *optIt};
}

Expand Down
1 change: 0 additions & 1 deletion src/engine/include/exchangesorchestrator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#include "market.hpp"
#include "queryresulttypes.hpp"
#include "threadpool.hpp"
#include "tradedamounts.hpp"
#include "withdrawoptions.hpp"

namespace cct {
Expand Down
2 changes: 1 addition & 1 deletion src/engine/include/traderesult.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ class TradeResult {
MonetaryAmount _from;
};

} // namespace cct
} // namespace cct
2 changes: 1 addition & 1 deletion src/engine/src/coincenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ void Coincenter::processCommand(const CoincenterCommand &cmd) {
cmd.tradeOptions());
break;
}
case CoincenterCommandType::kWithdraw: {
case CoincenterCommandType::kWithdrawApply: {
const auto &fromExchangeName = cmd.exchangeNames().front();
const auto &toExchangeName = cmd.exchangeNames().back();
const auto deliveredWithdrawInfoWithExchanges =
Expand Down
6 changes: 3 additions & 3 deletions src/engine/src/coincentercommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,15 @@ CoincenterCommand& CoincenterCommand::setTradeOptions(TradeOptions&& tradeOption
}

CoincenterCommand& CoincenterCommand::setWithdrawOptions(const WithdrawOptions& withdrawOptions) {
if (_type != CoincenterCommandType::kWithdraw) {
if (_type != CoincenterCommandType::kWithdrawApply) {
throw exception("Withdraw options can only be used for withdraws");
}
_specialOptions = withdrawOptions;
return *this;
}

CoincenterCommand& CoincenterCommand::setWithdrawOptions(WithdrawOptions&& withdrawOptions) {
if (_type != CoincenterCommandType::kWithdraw) {
if (_type != CoincenterCommandType::kWithdrawApply) {
throw exception("Withdraw options can only be used for withdraws");
}
_specialOptions = std::move(withdrawOptions);
Expand Down Expand Up @@ -181,7 +181,7 @@ CoincenterCommand& CoincenterCommand::setCur2(CurrencyCode cur2) {

CoincenterCommand& CoincenterCommand::setPercentageAmount(bool value) {
if (_type != CoincenterCommandType::kBuy && _type != CoincenterCommandType::kSell &&
_type != CoincenterCommandType::kTrade && _type != CoincenterCommandType::kWithdraw) {
_type != CoincenterCommandType::kTrade && _type != CoincenterCommandType::kWithdrawApply) {
throw exception("Percentage trade can only be set for trade / buy / sell or withdraw command");
}
_isPercentageAmount = value;
Expand Down
107 changes: 14 additions & 93 deletions src/engine/src/coincentercommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,15 @@
#include "coincentercommand.hpp"
#include "coincentercommandtype.hpp"
#include "coincenteroptions.hpp"
#include "coincenteroptionsdef.hpp"
#include "commandlineoptionsparser.hpp"
#include "commandlineoptionsparseriterator.hpp"
#include "depositsconstraints.hpp"
#include "exchangename.hpp"
#include "monetaryamount.hpp"
#include "ordersconstraints.hpp"
#include "priceoptions.hpp"
#include "priceoptionsdef.hpp"
#include "stringoptionparser.hpp"
#include "timedef.hpp"
#include "tradedefinitions.hpp"
#include "tradeoptions.hpp"
#include "withdrawoptions.hpp"
#include "withdrawsconstraints.hpp"

namespace cct {
Expand Down Expand Up @@ -58,7 +54,7 @@ vector<CoincenterCmdLineOptions> CoincenterCommands::ParseOptions(int argc, cons
if (groupParsedOptions.help) {
parser.displayHelp(programName, std::cout);
} else if (groupParsedOptions.version) {
CoincenterCmdLineOptions::PrintVersion(programName);
CoincenterCmdLineOptions::PrintVersion(programName, std::cout);
} else {
// Only store commands if they are not 'help' nor 'version'
parsedOptions.push_back(std::move(groupParsedOptions));
Expand Down Expand Up @@ -86,60 +82,6 @@ std::pair<OrdersConstraints, ExchangeNames> ParseOrderRequest(const CoincenterCm
std::get<2>(currenciesPrivateExchangesTuple));
}

TradeTypePolicy ComputeTradeTypePolicy(const CoincenterCmdLineOptions &cmdLineOptions) {
TradeTypePolicy tradeType = TradeTypePolicy::kDefault;
if (cmdLineOptions.forceMultiTrade) {
if (cmdLineOptions.forceSingleTrade) {
throw invalid_argument("Multi & Single trade cannot be forced at the same time");
}
if (cmdLineOptions.tradeAsync) {
throw invalid_argument("Cannot use force multi trade and asynchronous mode at the same time");
}
tradeType = TradeTypePolicy::kForceMultiTrade;
} else if (cmdLineOptions.forceSingleTrade || cmdLineOptions.tradeAsync) {
tradeType = TradeTypePolicy::kForceSingleTrade;
}

return tradeType;
}

TradeOptions ComputeTradeOptions(const CoincenterCmdLineOptions &cmdLineOptions, TradeTypePolicy tradeTypePolicy) {
TradeTimeoutAction timeoutAction =
cmdLineOptions.tradeTimeoutMatch ? TradeTimeoutAction::kForceMatch : TradeTimeoutAction::kCancel;
TradeMode tradeMode = cmdLineOptions.tradeSim ? TradeMode::kSimulation : TradeMode::kReal;
TradeSyncPolicy tradeSyncPolicy =
cmdLineOptions.tradeAsync ? TradeSyncPolicy::kAsynchronous : TradeSyncPolicy::kSynchronous;
if (!cmdLineOptions.tradeStrategy.empty()) {
PriceOptions priceOptions(cmdLineOptions.tradeStrategy);
return {priceOptions, timeoutAction, tradeMode, cmdLineOptions.tradeTimeout, cmdLineOptions.tradeUpdatePrice,
tradeTypePolicy, tradeSyncPolicy};
}
if (!cmdLineOptions.tradePrice.empty()) {
MonetaryAmount tradePrice(cmdLineOptions.tradePrice);
if (tradePrice.isAmountInteger() && tradePrice.hasNeutralCurrency()) {
// Then it must be a relative price
RelativePrice relativePrice = static_cast<RelativePrice>(tradePrice.integerPart());
PriceOptions priceOptions(relativePrice);
return {priceOptions, timeoutAction, tradeMode, cmdLineOptions.tradeTimeout, cmdLineOptions.tradeUpdatePrice,
tradeTypePolicy, tradeSyncPolicy};
}
if (cmdLineOptions.isSmartTrade()) {
throw invalid_argument("Absolute price is not compatible with smart buy / sell");
}
PriceOptions priceOptions(tradePrice);
return {priceOptions, timeoutAction, tradeMode, cmdLineOptions.tradeTimeout, cmdLineOptions.tradeUpdatePrice,
tradeTypePolicy, tradeSyncPolicy};
}
return {timeoutAction, tradeMode, cmdLineOptions.tradeTimeout, cmdLineOptions.tradeUpdatePrice,
tradeTypePolicy, tradeSyncPolicy};
}

WithdrawOptions ComputeWithdrawOptions(const CoincenterCmdLineOptions &cmdLineOptions) {
WithdrawSyncPolicy withdrawSyncPolicy =
cmdLineOptions.withdrawAsync ? WithdrawSyncPolicy::kAsynchronous : WithdrawSyncPolicy::kSynchronous;
return {cmdLineOptions.withdrawRefreshTime, withdrawSyncPolicy};
}

} // namespace

CoincenterCommands::CoincenterCommands(std::span<const CoincenterCmdLineOptions> cmdLineOptionsSpan) {
Expand Down Expand Up @@ -215,32 +157,11 @@ bool CoincenterCommands::addOption(const CoincenterCmdLineOptions &cmdLineOption
1) {
throw invalid_argument("Only one trade can be done at a time");
}
std::string_view tradeArgs;
bool isTradeAll = !cmdLineOptions.tradeAll.empty();
CoincenterCommandType commandType;
if (!cmdLineOptions.buy.empty()) {
tradeArgs = cmdLineOptions.buy;
commandType = CoincenterCommandType::kBuy;
} else if (!cmdLineOptions.sellAll.empty()) {
tradeArgs = cmdLineOptions.sellAll;
commandType = CoincenterCommandType::kSell;
} else if (!cmdLineOptions.sell.empty()) {
tradeArgs = cmdLineOptions.sell;
commandType = CoincenterCommandType::kSell;
} else {
tradeArgs = isTradeAll ? cmdLineOptions.tradeAll : cmdLineOptions.trade;
commandType = CoincenterCommandType::kTrade;
}
auto [tradeArgs, cmdType] = cmdLineOptions.getTradeArgStr();
if (!tradeArgs.empty()) {
if (!cmdLineOptions.tradeStrategy.empty() && !cmdLineOptions.tradePrice.empty()) {
throw invalid_argument("Trade price and trade strategy cannot be set together");
}

TradeTypePolicy tradeTypePolicy = ComputeTradeTypePolicy(cmdLineOptions);

CoincenterCommand &coincenterCommand = _commands.emplace_back(commandType);
CoincenterCommand &coincenterCommand = _commands.emplace_back(cmdType);

coincenterCommand.setTradeOptions(ComputeTradeOptions(cmdLineOptions, tradeTypePolicy));
coincenterCommand.setTradeOptions(cmdLineOptions.computeTradeOptions());

StringOptionParser optParser(tradeArgs);
if (cmdLineOptions.isSmartTrade()) {
Expand All @@ -257,7 +178,7 @@ bool CoincenterCommands::addOption(const CoincenterCmdLineOptions &cmdLineOption
}
coincenterCommand.setAmount(amount).setPercentageAmount(isPercentage).setExchangeNames(std::move(exchanges));
}
} else if (isTradeAll) {
} else if (!cmdLineOptions.tradeAll.empty()) {
auto [fromTradeCurrency, toTradeCurrency, exchanges] = optParser.getCurrenciesPrivateExchanges();
coincenterCommand.setAmount(MonetaryAmount(100, fromTradeCurrency))
.setPercentageAmount(true)
Expand Down Expand Up @@ -323,24 +244,24 @@ bool CoincenterCommands::addOption(const CoincenterCmdLineOptions &cmdLineOption
.setExchangeNames(std::move(exchanges));
}

if (!cmdLineOptions.withdraw.empty()) {
StringOptionParser anyParser(cmdLineOptions.withdraw);
if (!cmdLineOptions.withdrawApply.empty()) {
StringOptionParser anyParser(cmdLineOptions.withdrawApply);
auto [amountToWithdraw, isPercentageWithdraw, exchanges] = anyParser.getMonetaryAmountFromToPrivateExchange();
_commands.emplace_back(CoincenterCommandType::kWithdraw)
_commands.emplace_back(CoincenterCommandType::kWithdrawApply)
.setAmount(amountToWithdraw)
.setPercentageAmount(isPercentageWithdraw)
.setExchangeNames(std::move(exchanges))
.setWithdrawOptions(ComputeWithdrawOptions(cmdLineOptions));
.setWithdrawOptions(cmdLineOptions.computeWithdrawOptions());
}

if (!cmdLineOptions.withdrawAll.empty()) {
StringOptionParser anyParser(cmdLineOptions.withdrawAll);
if (!cmdLineOptions.withdrawApplyAll.empty()) {
StringOptionParser anyParser(cmdLineOptions.withdrawApplyAll);
auto [curToWithdraw, exchanges] = anyParser.getCurrencyFromToPrivateExchange();
_commands.emplace_back(CoincenterCommandType::kWithdraw)
_commands.emplace_back(CoincenterCommandType::kWithdrawApply)
.setAmount(MonetaryAmount(100, curToWithdraw))
.setPercentageAmount(true)
.setExchangeNames(std::move(exchanges))
.setWithdrawOptions(ComputeWithdrawOptions(cmdLineOptions));
.setWithdrawOptions(cmdLineOptions.computeWithdrawOptions());
}

if (!cmdLineOptions.dustSweeper.empty()) {
Expand Down
7 changes: 3 additions & 4 deletions src/engine/src/coincenterinfo_create.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace cct {
namespace {

json LoadGeneralConfigAndOverrideOptionsFromCLI(const CoincenterCmdLineOptions &cmdLineOptions) {
json generalConfigData = GeneralConfig::LoadFile(cmdLineOptions.dataDir);
json generalConfigData = GeneralConfig::LoadFile(cmdLineOptions.getDataDir());

// Override general config options from CLI
// Use at method to make sure to throw if key is not already present (it should)
Expand All @@ -52,7 +52,7 @@ MonitoringInfo MonitoringInfo_Create(std::string_view programName, const Coincen

CoincenterInfo CoincenterInfo_Create(std::string_view programName, const CoincenterCmdLineOptions &cmdLineOptions,
settings::RunMode runMode) {
const auto dataDir = cmdLineOptions.dataDir;
const auto dataDir = cmdLineOptions.getDataDir();

const json generalConfigData = LoadGeneralConfigAndOverrideOptionsFromCLI(cmdLineOptions);

Expand Down Expand Up @@ -87,8 +87,7 @@ CoincenterInfo CoincenterInfo_Create(std::string_view programName, const Coincen

ExchangeSecretsInfo ExchangeSecretsInfo_Create(const CoincenterCmdLineOptions &cmdLineOptions) {
if (cmdLineOptions.noSecrets) {
const StringOptionParser anyParser(*cmdLineOptions.noSecrets);

StringOptionParser anyParser(*cmdLineOptions.noSecrets);
return ExchangeSecretsInfo(anyParser.getExchanges());
}
return {};
Expand Down
Loading

0 comments on commit cefa95e

Please sign in to comment.