Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor and test coincenter options #476

Merged
merged 1 commit into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading