Skip to content

Commit

Permalink
Make some default trade options configurable in exchangeconfig file
Browse files Browse the repository at this point in the history
  • Loading branch information
sjanel committed Jan 21, 2024
1 parent dadd450 commit 0ebf44b
Show file tree
Hide file tree
Showing 75 changed files with 732 additions and 542 deletions.
30 changes: 17 additions & 13 deletions CONFIG.md

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions src/api-objects/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ add_unit_test(
coincenter_objects
)

add_unit_test(
tradeoptions_test
src/tradeoptions.cpp
test/tradeoptions_test.cpp
LIBRARIES
coincenter_objects
)

add_unit_test(
withdraw_test
test/withdraw_test.cpp
Expand Down
2 changes: 1 addition & 1 deletion src/api-objects/include/ordersconstraints.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class OrdersConstraints {

/// Build OrdersConstraints based on given filtering information
explicit OrdersConstraints(CurrencyCode cur1 = CurrencyCode(), CurrencyCode cur2 = CurrencyCode(),
Duration minAge = Duration(), Duration maxAge = Duration(),
Duration minAge = kUndefinedDuration, Duration maxAge = kUndefinedDuration,
OrderIdSet &&ordersIdSet = OrderIdSet());

TimePoint placedBefore() const { return _placedBefore; }
Expand Down
26 changes: 13 additions & 13 deletions src/api-objects/include/tradeoptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,27 @@
#include "tradedefinitions.hpp"

namespace cct {
class ExchangeConfig;
class TradeOptions {
public:
static constexpr Duration kDefaultTradeDuration = std::chrono::seconds(30);
static constexpr Duration kDefaultMinTimeBetweenPriceUpdates = std::chrono::seconds(5);

constexpr TradeOptions() noexcept = default;

constexpr explicit TradeOptions(const PriceOptions &priceOptions) : _priceOptions(priceOptions) {}

constexpr explicit TradeOptions(TradeMode tradeMode) : _mode(tradeMode) {}

TradeOptions(TradeTimeoutAction timeoutAction, TradeMode tradeMode, Duration dur, Duration minTimeBetweenPriceUpdates,
TradeTypePolicy tradeTypePolicy, TradeSyncPolicy tradeSyncPolicy = TradeSyncPolicy::kSynchronous);
constexpr explicit TradeOptions(TradeMode tradeMode) : _tradeMode(tradeMode) {}

/// Constructs a TradeOptions based on a continuously updated price from given string representation of trade
/// strategy
TradeOptions(const PriceOptions &priceOptions, TradeTimeoutAction timeoutAction, TradeMode tradeMode, Duration dur,
Duration minTimeBetweenPriceUpdates = kDefaultMinTimeBetweenPriceUpdates,
Duration minTimeBetweenPriceUpdates = kUndefinedDuration,
TradeTypePolicy tradeTypePolicy = TradeTypePolicy::kDefault,
TradeSyncPolicy tradeSyncPolicy = TradeSyncPolicy::kSynchronous);

/// Constructs a new TradeOptions based on 'rhs' with unspecified options overriden from exchange config values
TradeOptions(const TradeOptions &rhs, const ExchangeConfig &exchangeConfig);

constexpr Duration maxTradeTime() const { return _maxTradeTime; }

constexpr Duration minTimeBetweenPriceUpdates() const { return _minTimeBetweenPriceUpdates; }
Expand All @@ -42,7 +42,7 @@ class TradeOptions {

constexpr int relativePrice() const { return _priceOptions.relativePrice(); }

constexpr TradeMode tradeMode() const { return _mode; }
constexpr TradeMode tradeMode() const { return _tradeMode; }

constexpr TradeSyncPolicy tradeSyncPolicy() const { return _tradeSyncPolicy; }

Expand All @@ -52,13 +52,13 @@ class TradeOptions {
return _priceOptions.isTakerStrategy() && (!isSimulation() || !placeRealOrderInSimulationMode);
}

constexpr bool isSimulation() const { return _mode == TradeMode::kSimulation; }
constexpr bool isSimulation() const { return _tradeMode == TradeMode::kSimulation; }

constexpr bool isFixedPrice() const { return _priceOptions.isFixedPrice(); }

constexpr bool isRelativePrice() const { return _priceOptions.isRelativePrice(); }

constexpr bool placeMarketOrderAtTimeout() const { return _timeoutAction == TradeTimeoutAction::kForceMatch; }
constexpr bool placeMarketOrderAtTimeout() const { return _timeoutAction == TradeTimeoutAction::kMatch; }

constexpr void switchToTakerStrategy() { _priceOptions.switchToTakerStrategy(); }

Expand All @@ -71,11 +71,11 @@ class TradeOptions {
bool operator==(const TradeOptions &) const noexcept = default;

private:
Duration _maxTradeTime = kDefaultTradeDuration;
Duration _minTimeBetweenPriceUpdates = kDefaultMinTimeBetweenPriceUpdates;
Duration _maxTradeTime = kUndefinedDuration;
Duration _minTimeBetweenPriceUpdates = kUndefinedDuration;
PriceOptions _priceOptions;
TradeTimeoutAction _timeoutAction = TradeTimeoutAction::kCancel;
TradeMode _mode = TradeMode::kReal;
TradeTimeoutAction _timeoutAction = TradeTimeoutAction::kDefault;
TradeMode _tradeMode = TradeMode::kReal;
TradeTypePolicy _tradeTypePolicy = TradeTypePolicy::kDefault;
TradeSyncPolicy _tradeSyncPolicy = TradeSyncPolicy::kSynchronous;
};
Expand Down
7 changes: 4 additions & 3 deletions src/api-objects/include/withdrawsordepositsconstraints.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ class WithdrawsOrDepositsConstraints {
public:
using IdSet = FlatSet<string, std::less<>>;

explicit WithdrawsOrDepositsConstraints(CurrencyCode currencyCode = CurrencyCode(), Duration minAge = Duration(),
Duration maxAge = Duration(), IdSet &&idSet = IdSet());
explicit WithdrawsOrDepositsConstraints(CurrencyCode currencyCode = CurrencyCode(),
Duration minAge = kUndefinedDuration, Duration maxAge = kUndefinedDuration,
IdSet &&idSet = IdSet());

// Creates a WithdrawsOrDepositsConstraints based on a single transaction id and currency code.
// Useful for retrieval of a specific Deposit / Withdraw.
Expand All @@ -33,7 +34,7 @@ class WithdrawsOrDepositsConstraints {

bool validateCur(CurrencyCode cur) const { return _currencyCode.isNeutral() || cur == _currencyCode; }

bool validateTime(TimePoint t) const { return t >= _timeAfter && t <= _timeBefore; }
bool validateTime(TimePoint tp) const { return tp >= _timeAfter && tp <= _timeBefore; }

bool validateId(std::string_view id) const { return !isIdDefined() || _idSet.contains(id); }

Expand Down
6 changes: 3 additions & 3 deletions src/api-objects/src/ordersconstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ OrdersConstraints::OrdersConstraints(CurrencyCode cur1, CurrencyCode cur2, Durat
if (!_ordersIdSet.empty()) {
_orderConstraintsBitmap.set(OrderConstraintsBitmap::ConstraintType::kId);
}
auto now = Clock::now();
if (minAge != Duration()) {
const auto now = Clock::now();
if (minAge != kUndefinedDuration) {
_placedBefore = now - minAge;
_orderConstraintsBitmap.set(OrderConstraintsBitmap::ConstraintType::kPlacedBefore);
}
if (maxAge != Duration()) {
if (maxAge != kUndefinedDuration) {
_placedAfter = now - maxAge;
_orderConstraintsBitmap.set(OrderConstraintsBitmap::ConstraintType::kPlacedAfter);
}
Expand Down
34 changes: 21 additions & 13 deletions src/api-objects/src/tradeoptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,39 @@

#include "cct_string.hpp"
#include "durationstring.hpp"
#include "exchangeconfig.hpp"
#include "priceoptions.hpp"
#include "timedef.hpp"
#include "tradedefinitions.hpp"
#include "unreachable.hpp"

namespace cct {

TradeOptions::TradeOptions(TradeTimeoutAction timeoutAction, TradeMode tradeMode, Duration dur,
Duration minTimeBetweenPriceUpdates, TradeTypePolicy tradeTypePolicy,
TradeSyncPolicy tradeSyncPolicy)
: _maxTradeTime(dur),
_minTimeBetweenPriceUpdates(minTimeBetweenPriceUpdates),
_timeoutAction(timeoutAction),
_mode(tradeMode),
_tradeTypePolicy(tradeTypePolicy),
_tradeSyncPolicy(tradeSyncPolicy) {}

TradeOptions::TradeOptions(const PriceOptions &priceOptions, TradeTimeoutAction timeoutAction, TradeMode tradeMode,
Duration dur, Duration minTimeBetweenPriceUpdates, TradeTypePolicy tradeTypePolicy,
TradeSyncPolicy tradeSyncPolicy)
: _maxTradeTime(dur),
_minTimeBetweenPriceUpdates(minTimeBetweenPriceUpdates),
_priceOptions(priceOptions),
_timeoutAction(timeoutAction),
_mode(tradeMode),
_tradeMode(tradeMode),
_tradeTypePolicy(tradeTypePolicy),
_tradeSyncPolicy(tradeSyncPolicy) {}

TradeOptions::TradeOptions(const TradeOptions &rhs, const ExchangeConfig &exchangeConfig)
: _maxTradeTime(rhs._maxTradeTime == kUndefinedDuration ? exchangeConfig.tradeConfig().timeout()
: rhs._maxTradeTime),
_minTimeBetweenPriceUpdates(rhs._minTimeBetweenPriceUpdates == kUndefinedDuration
? exchangeConfig.tradeConfig().minPriceUpdateDuration()
: rhs._minTimeBetweenPriceUpdates),
_priceOptions(rhs._priceOptions.isDefault() ? PriceOptions(exchangeConfig.tradeConfig()) : rhs._priceOptions),
_timeoutAction(rhs._timeoutAction == TradeTimeoutAction::kDefault
? exchangeConfig.tradeConfig().tradeTimeoutAction()
: rhs._timeoutAction),
_tradeMode(rhs._tradeMode),
_tradeTypePolicy(rhs._tradeTypePolicy),
_tradeSyncPolicy(rhs._tradeSyncPolicy) {}

bool TradeOptions::isMultiTradeAllowed(bool multiTradeAllowedByDefault) const {
switch (_tradeTypePolicy) {
case TradeTypePolicy::kDefault:
Expand All @@ -47,10 +52,13 @@ bool TradeOptions::isMultiTradeAllowed(bool multiTradeAllowedByDefault) const {

std::string_view TradeOptions::timeoutActionStr() const {
switch (_timeoutAction) {
case TradeTimeoutAction::kDefault:
// Default will behave the same as cancel - this field is not publicly exposed
[[fallthrough]];
case TradeTimeoutAction::kCancel:
return "cancel";
case TradeTimeoutAction::kForceMatch:
return "force-match";
case TradeTimeoutAction::kMatch:
return "match";
default:
unreachable();
}
Expand Down
6 changes: 3 additions & 3 deletions src/api-objects/src/withdrawsordepositsconstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ WithdrawsOrDepositsConstraints::WithdrawsOrDepositsConstraints(CurrencyCode curr
if (!_idSet.empty()) {
_currencyIdTimeConstraintsBmp.set(CurrencyIdTimeConstraintsBmp::ConstraintType::kId);
}
auto now = Clock::now();
if (minAge != Duration()) {
const auto now = Clock::now();
if (minAge != kUndefinedDuration) {
_timeBefore = now - minAge;
_currencyIdTimeConstraintsBmp.set(CurrencyIdTimeConstraintsBmp::ConstraintType::kReceivedBefore);
}
if (maxAge != Duration()) {
if (maxAge != kUndefinedDuration) {
_timeAfter = now - maxAge;
_currencyIdTimeConstraintsBmp.set(CurrencyIdTimeConstraintsBmp::ConstraintType::kReceivedAfter);
}
Expand Down
12 changes: 12 additions & 0 deletions src/api-objects/test/tradeoptions_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include "tradeoptions.hpp"

#include <gtest/gtest.h>

namespace cct {
TEST(TradeOptionsTest, DefaultTradeTimeoutAction) {
TradeOptions tradeOptions;

EXPECT_FALSE(tradeOptions.placeMarketOrderAtTimeout());
EXPECT_EQ(tradeOptions.timeoutActionStr(), "cancel");
}
} // namespace cct
4 changes: 2 additions & 2 deletions src/api/common/include/exchangeprivateapi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#include "currencyexchangeflatset.hpp"
#include "depositsconstraints.hpp"
#include "exchangebase.hpp"
#include "exchangeinfo.hpp"
#include "exchangeconfig.hpp"
#include "exchangeprivateapitypes.hpp"
#include "exchangepublicapi.hpp"
#include "exchangepublicapitypes.hpp"
Expand Down Expand Up @@ -118,7 +118,7 @@ class ExchangePrivate : public ExchangeBase {
/// Builds an ExchangeName wrapping the exchange and the key name
ExchangeName exchangeName() const { return {_exchangePublic.name(), _apiKey.name()}; }

const ExchangeInfo &exchangeInfo() const { return _exchangePublic.exchangeInfo(); }
const ExchangeConfig &exchangeConfig() const { return _exchangePublic.exchangeConfig(); }

protected:
ExchangePrivate(const CoincenterInfo &coincenterInfo, ExchangePublic &exchangePublic, const APIKey &apiKey);
Expand Down
6 changes: 3 additions & 3 deletions src/api/common/include/exchangepublicapi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
namespace cct {

class CoincenterInfo;
class ExchangeInfo;
class ExchangeConfig;
class FiatConverter;

namespace api {
Expand Down Expand Up @@ -161,7 +161,7 @@ class ExchangePublic : public ExchangeBase {

const CoincenterInfo &coincenterInfo() const { return _coincenterInfo; }

const ExchangeInfo &exchangeInfo() const { return _exchangeInfo; }
const ExchangeConfig &exchangeConfig() const { return _exchangeConfig; }

CommonAPI &commonAPI() { return _commonApi; }

Expand All @@ -180,7 +180,7 @@ class ExchangePublic : public ExchangeBase {
FiatConverter &_fiatConverter;
CommonAPI &_commonApi;
const CoincenterInfo &_coincenterInfo;
const ExchangeInfo &_exchangeInfo;
const ExchangeConfig &_exchangeConfig;
std::mutex _tradableMarketsMutex;
std::mutex _allOrderBooksMutex;
};
Expand Down
31 changes: 18 additions & 13 deletions src/api/common/src/exchangeprivateapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#include "depositsconstraints.hpp"
#include "durationstring.hpp"
#include "exchangebase.hpp"
#include "exchangeinfo.hpp"
#include "exchangeconfig.hpp"
#include "exchangename.hpp"
#include "exchangeprivateapitypes.hpp"
#include "exchangepublicapi.hpp"
Expand All @@ -40,6 +40,7 @@
#include "tradedamounts.hpp"
#include "tradedefinitions.hpp"
#include "tradeinfo.hpp"
#include "tradeoptions.hpp"
#include "tradeside.hpp"
#include "unreachable.hpp"
#include "withdraw.hpp"
Expand Down Expand Up @@ -85,14 +86,17 @@ void ExchangePrivate::addBalance(BalancePortfolio &balancePortfolio, MonetaryAmo

TradedAmounts ExchangePrivate::trade(MonetaryAmount from, CurrencyCode toCurrency, const TradeOptions &options,
const MarketsPath &conversionPath) {
const bool realOrderPlacedInSimulationMode = !isSimulatedOrderSupported() && exchangeInfo().placeSimulateRealOrder();
// Use exchange config settings for un-overriden trade options
const auto &exchangeConfig = this->exchangeConfig();
const TradeOptions actualOptions(options, exchangeConfig);
const bool realOrderPlacedInSimulationMode = !isSimulatedOrderSupported() && exchangeConfig.placeSimulateRealOrder();
const int nbTrades = static_cast<int>(conversionPath.size());
const bool isMultiTradeAllowed = options.isMultiTradeAllowed(exchangeInfo().multiTradeAllowedByDefault());
const bool isMultiTradeAllowed = actualOptions.isMultiTradeAllowed(exchangeConfig.multiTradeAllowedByDefault());

ExchangeName exchangeName = this->exchangeName();
log::info("{}rade {} -> {} on {} requested", isMultiTradeAllowed && nbTrades > 1 ? "Multi t" : "T", from, toCurrency,
exchangeName);
log::debug(options.str(realOrderPlacedInSimulationMode));
log::debug(actualOptions.str(realOrderPlacedInSimulationMode));

TradedAmounts tradedAmounts(from.currencyCode(), toCurrency);
if (conversionPath.empty()) {
Expand All @@ -108,7 +112,7 @@ TradedAmounts ExchangePrivate::trade(MonetaryAmount from, CurrencyCode toCurrenc
for (int tradePos = 0; tradePos < nbTrades; ++tradePos) {
Market mk = conversionPath[tradePos];
log::info("Step {}/{} - trade {} into {}", tradePos + 1, nbTrades, avAmount, mk.opposite(avAmount.currencyCode()));
TradedAmounts stepTradedAmounts = marketTrade(avAmount, options, mk);
TradedAmounts stepTradedAmounts = marketTrade(avAmount, actualOptions, mk);
avAmount = stepTradedAmounts.to;
if (avAmount == 0) {
break;
Expand All @@ -135,7 +139,7 @@ TradedAmounts ExchangePrivate::marketTrade(MonetaryAmount from, const TradeOptio
TradeContext tradeContext(mk, side, userRef);
TradeInfo tradeInfo(tradeContext, tradeOptions);
TradeOptions &options = tradeInfo.options;
const bool placeSimulatedRealOrder = exchangeInfo().placeSimulateRealOrder();
const bool placeSimulatedRealOrder = exchangeConfig().placeSimulateRealOrder();

enum class NextAction : int8_t { kPlaceInitialOrder, kPlaceLimitOrder, kPlaceMarketOrder, kWait };

Expand Down Expand Up @@ -447,8 +451,9 @@ TradedAmounts ExchangePrivate::buySomeAmountToMakeFutureSellPossible(
}

TradedAmountsVectorWithFinalAmount ExchangePrivate::queryDustSweeper(CurrencyCode currencyCode) {
const MonetaryAmountByCurrencySet &dustThresholds = exchangeInfo().dustAmountsThreshold();
const int dustSweeperMaxNbTrades = exchangeInfo().dustSweeperMaxNbTrades();
const auto &exchangeConfig = this->exchangeConfig();
const MonetaryAmountByCurrencySet &dustThresholds = exchangeConfig.dustAmountsThreshold();
const int dustSweeperMaxNbTrades = exchangeConfig.dustSweeperMaxNbTrades();
const auto dustThresholdLb = dustThresholds.find(MonetaryAmount(0, currencyCode));
const auto eName = exchangeName();

Expand All @@ -460,7 +465,7 @@ TradedAmountsVectorWithFinalAmount ExchangePrivate::queryDustSweeper(CurrencyCod
const MonetaryAmount dustThreshold = *dustThresholdLb;

PriceOptions priceOptions(PriceStrategy::kTaker);
TradeOptions tradeOptions(priceOptions);
TradeOptions tradeOptions(TradeOptions{priceOptions}, exchangeConfig);
MarketSet markets = _exchangePublic.queryTradableMarkets();
MarketPriceMap marketPriceMap;
bool checkAmountBalanceAgainstDustThreshold = true;
Expand Down Expand Up @@ -528,7 +533,7 @@ PlaceOrderInfo ExchangePrivate::placeOrderProcess(MonetaryAmount &from, Monetary
const MonetaryAmount volume(isSell ? from : MonetaryAmount(from / price, mk.base()));

if (tradeInfo.options.isSimulation() && !isSimulatedOrderSupported()) {
if (exchangeInfo().placeSimulateRealOrder()) {
if (exchangeConfig().placeSimulateRealOrder()) {
log::debug("Place simulate real order - price {} will be overriden", price);
MarketOrderBook marketOrderbook = _exchangePublic.queryOrderBook(mk);
price = isSell ? marketOrderbook.getHighestTheoreticalPrice() : marketOrderbook.getLowestTheoreticalPrice();
Expand All @@ -552,13 +557,13 @@ PlaceOrderInfo ExchangePrivate::placeOrderProcess(MonetaryAmount &from, Monetary

PlaceOrderInfo ExchangePrivate::computeSimulatedMatchedPlacedOrderInfo(MonetaryAmount volume, MonetaryAmount price,
const TradeInfo &tradeInfo) const {
const bool placeSimulatedRealOrder = exchangeInfo().placeSimulateRealOrder();
const bool placeSimulatedRealOrder = exchangeConfig().placeSimulateRealOrder();
const bool isTakerStrategy = tradeInfo.options.isTakerStrategy(placeSimulatedRealOrder);
const bool isSell = tradeInfo.tradeContext.side == TradeSide::kSell;

MonetaryAmount toAmount = isSell ? volume.convertTo(price) : volume;
ExchangeInfo::FeeType feeType = isTakerStrategy ? ExchangeInfo::FeeType::kTaker : ExchangeInfo::FeeType::kMaker;
toAmount = _coincenterInfo.exchangeInfo(_exchangePublic.name()).applyFee(toAmount, feeType);
ExchangeConfig::FeeType feeType = isTakerStrategy ? ExchangeConfig::FeeType::kTaker : ExchangeConfig::FeeType::kMaker;
toAmount = _coincenterInfo.exchangeConfig(_exchangePublic.name()).applyFee(toAmount, feeType);
PlaceOrderInfo placeOrderInfo(OrderInfo(TradedAmounts(isSell ? volume : volume.toNeutral() * price, toAmount)),
OrderId("SimulatedOrderId"));
placeOrderInfo.setClosed();
Expand Down
Loading

0 comments on commit 0ebf44b

Please sign in to comment.