Skip to content

Commit

Permalink
Improve returned trade result information
Browse files Browse the repository at this point in the history
  • Loading branch information
sjanel committed Oct 27, 2023
1 parent 90f37b7 commit 25e9fc6
Show file tree
Hide file tree
Showing 32 changed files with 510 additions and 428 deletions.
27 changes: 11 additions & 16 deletions src/api-objects/include/tradedamounts.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,24 @@
namespace cct {

struct TradedAmounts {
constexpr TradedAmounts() noexcept(std::is_nothrow_default_constructible_v<MonetaryAmount>) = default;
constexpr TradedAmounts() noexcept = default;

constexpr TradedAmounts(CurrencyCode fromCurrencyCode, CurrencyCode toCurrencyCode)
: tradedFrom(0, fromCurrencyCode), tradedTo(0, toCurrencyCode) {}
: from(0, fromCurrencyCode), to(0, toCurrencyCode) {}

constexpr TradedAmounts(MonetaryAmount fromAmount, MonetaryAmount toAmount)
: tradedFrom(fromAmount), tradedTo(toAmount) {}
constexpr TradedAmounts(MonetaryAmount fromAmount, MonetaryAmount toAmount) : from(fromAmount), to(toAmount) {}

TradedAmounts operator+(const TradedAmounts &o) const {
return TradedAmounts(tradedFrom + o.tradedFrom, tradedTo + o.tradedTo);
}
TradedAmounts &operator+=(const TradedAmounts &o) {
*this = *this + o;
return *this;
}
TradedAmounts operator+(const TradedAmounts &rhs) const { return {from + rhs.from, to + rhs.to}; }
TradedAmounts &operator+=(const TradedAmounts &rhs) { return (*this = *this + rhs); }

constexpr bool operator==(const TradedAmounts &) const = default;
constexpr bool operator==(const TradedAmounts &) const noexcept = default;

friend std::ostream &operator<<(std::ostream &os, const TradedAmounts &tradedAmounts);

string str() const;

MonetaryAmount tradedFrom; // In currency of 'from' amount
MonetaryAmount tradedTo; // In the opposite currency
MonetaryAmount from; // In currency of 'from' amount
MonetaryAmount to; // In the opposite currency
};

} // namespace cct
Expand All @@ -42,7 +36,8 @@ struct TradedAmounts {
template <>
struct fmt::formatter<cct::TradedAmounts> {
constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin(), end = ctx.end();
const auto it = ctx.begin();
const auto end = ctx.end();
if (it != end && *it != '}') {
throw format_error("invalid format");
}
Expand All @@ -51,7 +46,7 @@ struct fmt::formatter<cct::TradedAmounts> {

template <typename FormatContext>
auto format(const cct::TradedAmounts &a, FormatContext &ctx) const -> decltype(ctx.out()) {
return fmt::format_to(ctx.out(), "{} -> {}", a.tradedFrom, a.tradedTo);
return fmt::format_to(ctx.out(), "{} -> {}", a.from, a.to);
}
};
#endif
7 changes: 3 additions & 4 deletions src/api-objects/src/tradedamounts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@
namespace cct {

string TradedAmounts::str() const {
string ret = tradedFrom.str();
string ret = from.str();
ret.append(" -> ");
ret.append(tradedTo.str());
to.appendStrTo(ret);
return ret;
}

std::ostream &operator<<(std::ostream &os, const TradedAmounts &tradedAmounts) {
os << tradedAmounts.tradedFrom << " -> " << tradedAmounts.tradedTo;
return os;
return os << tradedAmounts.from << " -> " << tradedAmounts.to;
}

} // namespace cct
20 changes: 10 additions & 10 deletions src/api/common/src/exchangeprivateapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,15 @@ TradedAmounts ExchangePrivate::trade(MonetaryAmount from, CurrencyCode toCurrenc
Market mk = conversionPath[tradePos];
log::info("Step {}/{} - trade {} into {}", tradePos + 1, nbTrades, avAmount, mk.opposite(avAmount.currencyCode()));
TradedAmounts stepTradedAmounts = marketTrade(avAmount, options, mk);
avAmount = stepTradedAmounts.tradedTo;
avAmount = stepTradedAmounts.to;
if (avAmount == 0) {
break;
}
if (tradePos == 0) {
tradedAmounts.tradedFrom = stepTradedAmounts.tradedFrom;
tradedAmounts.from = stepTradedAmounts.from;
}
if (tradePos + 1 == nbTrades) {
tradedAmounts.tradedTo = stepTradedAmounts.tradedTo;
tradedAmounts.to = stepTradedAmounts.to;
}
}
return tradedAmounts;
Expand Down Expand Up @@ -220,7 +220,7 @@ TradedAmounts ExchangePrivate::marketTrade(MonetaryAmount from, const TradeOptio
log::debug("Cancel order {}", orderId);
OrderInfo cancelledOrderInfo = cancelOrder(orderId, tradeContext);
totalTradedAmounts += cancelledOrderInfo.tradedAmounts;
from -= cancelledOrderInfo.tradedAmounts.tradedFrom;
from -= cancelledOrderInfo.tradedAmounts.from;
if (from == 0) {
log::debug("Order {} matched with last traded amounts {} while cancelling", orderId,
cancelledOrderInfo.tradedAmounts);
Expand Down Expand Up @@ -380,7 +380,7 @@ std::pair<TradedAmounts, Market> ExchangePrivate::isSellingPossibleOneShotDustSw
for (Market mk : possibleMarkets) {
log::info("Dust sweeper - attempt to sell in one shot on {}", mk);
TradedAmounts tradedAmounts = marketTrade(amountBalance, tradeOptions, mk);
if (tradedAmounts.tradedTo != 0) {
if (tradedAmounts.to != 0) {
return {tradedAmounts, mk};
}
}
Expand Down Expand Up @@ -432,7 +432,7 @@ TradedAmounts ExchangePrivate::buySomeAmountToMakeFutureSellPossible(
log::info("Dust sweeper - attempt to buy some {} for future selling", currencyCode);
TradedAmounts tradedAmounts = marketTrade(fromAmount, tradeOptions, mk);

if (tradedAmounts.tradedTo != 0) {
if (tradedAmounts.to != 0) {
// Then we should have sufficient amount now on this market
return tradedAmounts;
}
Expand Down Expand Up @@ -500,7 +500,7 @@ TradedAmountsVectorWithFinalAmount ExchangePrivate::queryDustSweeper(CurrencyCod
TradedAmounts tradedAmounts;
std::tie(tradedAmounts, tradedMarket) =
isSellingPossibleOneShotDustSweeper(possibleMarkets, ret.finalAmount, tradeOptions);
if (tradedAmounts.tradedFrom != 0) {
if (tradedAmounts.from != 0) {
IncrementPenalty(tradedMarket, penaltyPerMarketMap);
ret.tradedAmountsVector.push_back(std::move(tradedAmounts));
continue;
Expand All @@ -510,7 +510,7 @@ TradedAmountsVectorWithFinalAmount ExchangePrivate::queryDustSweeper(CurrencyCod
// Selling has not worked - so we need to buy some amount on the requested currency first
tradedAmounts = buySomeAmountToMakeFutureSellPossible(possibleMarkets, marketPriceMap, dustThreshold, balance,
tradeOptions, dustThresholds);
if (tradedAmounts.tradedFrom == 0) {
if (tradedAmounts.from == 0) {
break;
}
ret.tradedAmountsVector.push_back(std::move(tradedAmounts));
Expand All @@ -532,7 +532,7 @@ PlaceOrderInfo ExchangePrivate::placeOrderProcess(MonetaryAmount &from, Monetary
price = isSell ? marketOrderbook.getHighestTheoreticalPrice() : marketOrderbook.getLowestTheoreticalPrice();
} else {
PlaceOrderInfo placeOrderInfo = computeSimulatedMatchedPlacedOrderInfo(volume, price, tradeInfo);
from -= placeOrderInfo.tradedAmounts().tradedFrom;
from -= placeOrderInfo.tradedAmounts().from;
return placeOrderInfo;
}
}
Expand All @@ -544,7 +544,7 @@ PlaceOrderInfo ExchangePrivate::placeOrderProcess(MonetaryAmount &from, Monetary
// (and remove the need to implement the matching amount computation with fees for each exchange)
placeOrderInfo = computeSimulatedMatchedPlacedOrderInfo(volume, price, tradeInfo);
}
from -= placeOrderInfo.tradedAmounts().tradedFrom;
from -= placeOrderInfo.tradedAmounts().from;
return placeOrderInfo;
}

Expand Down
3 changes: 2 additions & 1 deletion src/api/common/src/exchangepublicapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ MarketsPath ExchangePublic::findMarketsPath(CurrencyCode fromCurrency, CurrencyC
std::pop_heap(searchPaths.begin(), searchPaths.end(), comp);
CurrencyDirPath path = std::move(searchPaths.back());
searchPaths.pop_back();

CurrencyCode lastCurrencyCode = path.back().cur;
if (visitedCurrencies.contains(lastCurrencyCode)) {
continue;
Expand Down Expand Up @@ -159,7 +160,7 @@ MarketsPath ExchangePublic::findMarketsPath(CurrencyCode fromCurrency, CurrencyC
std::push_heap(searchPaths.begin(), searchPaths.end(), comp);
}
}
std::optional<CurrencyCode> optLastFiat =
const std::optional<CurrencyCode> optLastFiat =
considerStableCoinsAsFiats ? _coincenterInfo.fiatCurrencyIfStableCoin(lastCurrencyCode) : std::nullopt;
const bool isLastFiatLike = optLastFiat || fiats.contains(lastCurrencyCode);
if (isToFiatLike && isLastFiatLike) {
Expand Down
19 changes: 9 additions & 10 deletions src/api/common/test/exchangeprivateapi_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ inline bool operator==(const DeliveredWithdrawInfo &lhs, const DeliveredWithdraw

inline BalancePortfolio operator+(const BalancePortfolio &balancePortfolio, const TradedAmounts &tradedAmounts) {
BalancePortfolio ret = balancePortfolio;
ret.add(tradedAmounts.tradedTo);
ret.add(-tradedAmounts.tradedFrom);
ret.add(tradedAmounts.to);
ret.add(-tradedAmounts.from);
return ret;
}

Expand Down Expand Up @@ -684,7 +684,7 @@ TEST_F(ExchangePrivateDustSweeperTest, DustSweeperDirectSellingPossible) {
MonetaryAmount avBtcAmount{75, "BTC", 4};
EXPECT_CALL(exchangePrivate, queryAccountBalance(balanceOptions))
.WillOnce(testing::Return(BalancePortfolio{from, avBtcAmount}))
.WillOnce(testing::Return(BalancePortfolio{avBtcAmount + tradedAmounts.tradedTo}));
.WillOnce(testing::Return(BalancePortfolio{avBtcAmount + tradedAmounts.to}));

TradedAmountsVector tradedAmountsVector{tradedAmounts};
TradedAmountsVectorWithFinalAmount res{tradedAmountsVector, MonetaryAmount{0, dustCur}};
Expand All @@ -710,14 +710,13 @@ TEST_F(ExchangePrivateDustSweeperTest, DustSweeper2StepsSameMarket) {
MonetaryAmount xrpDustThreshold = dustThreshold(dustCur).value_or(MonetaryAmount{-1});
TradedAmounts tradedAmounts1 = expectTakerBuy(xrpDustThreshold, xrpbtcAskPri, xrpbtcBidPri, xrpbtcMarket);

TradedAmounts tradedAmounts2 = expectTakerSell(from + tradedAmounts1.tradedTo, pri);
TradedAmounts tradedAmounts2 = expectTakerSell(from + tradedAmounts1.to, pri);

MonetaryAmount avBtcAmount{75, "BTC", 4};
EXPECT_CALL(exchangePrivate, queryAccountBalance(balanceOptions))
.WillOnce(testing::Return(BalancePortfolio{from, avBtcAmount}))
.WillOnce(
testing::Return(BalancePortfolio{from + tradedAmounts1.tradedTo, avBtcAmount - tradedAmounts1.tradedFrom}))
.WillOnce(testing::Return(BalancePortfolio{avBtcAmount - tradedAmounts1.tradedFrom}));
.WillOnce(testing::Return(BalancePortfolio{from + tradedAmounts1.to, avBtcAmount - tradedAmounts1.from}))
.WillOnce(testing::Return(BalancePortfolio{avBtcAmount - tradedAmounts1.from}));

TradedAmountsVector tradedAmountsVector{tradedAmounts1, tradedAmounts2};
TradedAmountsVectorWithFinalAmount res{tradedAmountsVector, MonetaryAmount{0, dustCur}};
Expand Down Expand Up @@ -762,15 +761,15 @@ TEST_F(ExchangePrivateDustSweeperTest, DustSweeper5Steps) {

MonetaryAmount xrpDustThreshold = dustThreshold(dustCur).value_or(MonetaryAmount{-1});
TradedAmounts tradedAmounts1 = expectTakerBuy(xrpDustThreshold, xrpbtcAskPri, xrpbtcBidPri, xrpbtcMarket);
from += tradedAmounts1.tradedTo;
from += tradedAmounts1.to;

BalancePortfolio balance2 = balance1 + tradedAmounts1;

EXPECT_CALL(exchangePrivate, queryAccountBalance(balanceOptions)).WillOnce(testing::Return(balance2));

int percentXRPSoldSecondStep = 80;
TradedAmounts tradedAmounts2 = expectTakerSell(from, priBtc, percentXRPSoldSecondStep);
from -= tradedAmounts2.tradedFrom;
from -= tradedAmounts2.from;

BalancePortfolio balance3 = balance2 + tradedAmounts2;

Expand All @@ -784,7 +783,7 @@ TEST_F(ExchangePrivateDustSweeperTest, DustSweeper5Steps) {
expectTakerBuy(xrpDustThreshold, xrpbtcAskPri, xrpbtcBidPri, xrpbtcMarket, false);

TradedAmounts tradedAmounts3 = expectTakerBuy((3 * xrpDustThreshold) / 2, xrpeurAskPri, xrpeurBidPri, xrpeurMarket);
from += tradedAmounts3.tradedTo;
from += tradedAmounts3.to;

BalancePortfolio balance4 = balance3 + tradedAmounts3;
EXPECT_CALL(exchangePrivate, queryAccountBalance(balanceOptions)).WillOnce(testing::Return(balance4));
Expand Down
8 changes: 4 additions & 4 deletions src/api/exchanges/src/binanceprivateapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -576,10 +576,10 @@ TradedAmounts ParseTrades(Market mk, CurrencyCode fromCurrencyCode, const json&
MonetaryAmount fee(fillDetail["commission"].get<std::string_view>(),
fillDetail["commissionAsset"].get<std::string_view>());
log::debug("Gross {} has been matched at {} price, with a fee of {}", quantity, price, fee);
if (fee.currencyCode() == detailTradedInfo.tradedFrom.currencyCode()) {
detailTradedInfo.tradedFrom += fee;
} else if (fee.currencyCode() == detailTradedInfo.tradedTo.currencyCode()) {
detailTradedInfo.tradedTo -= fee;
if (fee.currencyCode() == detailTradedInfo.from.currencyCode()) {
detailTradedInfo.from += fee;
} else if (fee.currencyCode() == detailTradedInfo.to.currencyCode()) {
detailTradedInfo.to -= fee;
} else {
log::debug("Fee is deduced from {} which is outside {}, do not count it in this trade", fee.currencyStr(), mk);
}
Expand Down
8 changes: 4 additions & 4 deletions src/api/exchanges/src/bithumbprivateapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -900,11 +900,11 @@ OrderInfo BithumbPrivate::queryOrderInfo(OrderIdView orderId, const TradeContext
MonetaryAmount fee(contractDetail["fee"].get<std::string_view>(), feeCurrency);

if (fromCurrencyCode == mk.quote()) {
orderInfo.tradedAmounts.tradedFrom += tradedCost + fee;
orderInfo.tradedAmounts.tradedTo += tradedVol;
orderInfo.tradedAmounts.from += tradedCost + fee;
orderInfo.tradedAmounts.to += tradedVol;
} else {
orderInfo.tradedAmounts.tradedFrom += tradedVol;
orderInfo.tradedAmounts.tradedTo += tradedCost - fee;
orderInfo.tradedAmounts.from += tradedVol;
orderInfo.tradedAmounts.to += tradedCost - fee;
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/api/exchanges/src/krakenprivateapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,11 +566,11 @@ OrderInfo KrakenPrivate::queryOrderInfo(OrderIdView orderId, const TradeContext&

if (fromCurrencyCode == mk.quote()) {
MonetaryAmount price(orderJson["price"].get<std::string_view>(), mk.base());
orderInfo.tradedAmounts.tradedFrom += tradedCost;
orderInfo.tradedAmounts.tradedTo += (tradedCost - fee).toNeutral() / price;
orderInfo.tradedAmounts.from += tradedCost;
orderInfo.tradedAmounts.to += (tradedCost - fee).toNeutral() / price;
} else {
orderInfo.tradedAmounts.tradedFrom += tradedVol;
orderInfo.tradedAmounts.tradedTo += tradedCost - fee;
orderInfo.tradedAmounts.from += tradedVol;
orderInfo.tradedAmounts.to += tradedCost - fee;
}
}

Expand Down
11 changes: 2 additions & 9 deletions src/api/exchanges/src/kucoinpublicapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,15 +285,8 @@ MarketOrderBook KucoinPublic::OrderBookFunc::operator()(Market mk, int depth) {
using OrderBookVec = vector<OrderBookLine>;
OrderBookVec orderBookLines;
orderBookLines.reserve(static_cast<OrderBookVec::size_type>(depth) * 2);
for (auto asksOrBids : {std::addressof(bids), std::addressof(asks)}) {
const bool isAsk = asksOrBids == std::addressof(asks);
if (isAsk) {
FillOrderBook(mk, depth, isAsk, asksOrBids->begin(), asksOrBids->end(), orderBookLines);
} else {
// Reverse iterate as they are received in descending order
FillOrderBook(mk, depth, isAsk, asksOrBids->rbegin(), asksOrBids->rend(), orderBookLines);
}
}
FillOrderBook(mk, depth, false, bids.begin(), bids.end(), orderBookLines);
FillOrderBook(mk, depth, true, asks.begin(), asks.end(), orderBookLines);
return MarketOrderBook(mk, orderBookLines);
}

Expand Down
12 changes: 6 additions & 6 deletions src/api/exchanges/src/upbitprivateapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -470,17 +470,17 @@ OrderInfo ParseOrderJson(const json& orderJson, CurrencyCode fromCurrencyCode, M
MonetaryAmount tradedCost(orderDetails["funds"].get<std::string_view>(), mk.quote());

if (fromCurrencyCode == mk.quote()) {
orderInfo.tradedAmounts.tradedFrom += tradedCost;
orderInfo.tradedAmounts.tradedTo += tradedVol;
orderInfo.tradedAmounts.from += tradedCost;
orderInfo.tradedAmounts.to += tradedVol;
} else {
orderInfo.tradedAmounts.tradedFrom += tradedVol;
orderInfo.tradedAmounts.tradedTo += tradedCost;
orderInfo.tradedAmounts.from += tradedVol;
orderInfo.tradedAmounts.to += tradedCost;
}
}
if (fromCurrencyCode == mk.quote()) {
orderInfo.tradedAmounts.tradedFrom += fee;
orderInfo.tradedAmounts.from += fee;
} else {
orderInfo.tradedAmounts.tradedTo -= fee;
orderInfo.tradedAmounts.to -= fee;
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/api/exchanges/test/commonapi_test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,8 @@ class TestAPI {
TradeOptions tradeOptions(TradeMode::kSimulation);
MonetaryAmount smallFrom = smallAmountIt->amount() / 100;
MonetaryAmount bigFrom = bigAmountIt->amount().toNeutral() * bigAmountIt->price() * 100;
EXPECT_GT(exchangePrivateOpt->trade(smallFrom, mk.quote(), tradeOptions).tradedTo, 0);
EXPECT_NE(exchangePrivateOpt->trade(bigFrom, mk.base(), tradeOptions).tradedFrom, 0);
EXPECT_GT(exchangePrivateOpt->trade(smallFrom, mk.quote(), tradeOptions).to, 0);
EXPECT_NE(exchangePrivateOpt->trade(bigFrom, mk.base(), tradeOptions).from, 0);
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/api/interface/include/exchange.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,22 @@ class Exchange {
const api::ExchangePublic &apiPublic() const { return _exchangePublic; }

api::ExchangePrivate &apiPrivate() {
if (_pExchangePrivate) {
if (hasPrivateAPI()) {
return *_pExchangePrivate;
}
throw exception("No private key associated to exchange {}", name());
}

const api::ExchangePrivate &apiPrivate() const {
if (_pExchangePrivate) {
if (hasPrivateAPI()) {
return *_pExchangePrivate;
}
throw exception("No private key associated to exchange {}", name());
}

const ExchangeInfo &exchangeInfo() const { return _exchangeInfo; }

bool hasPrivateAPI() const { return _pExchangePrivate; }
bool hasPrivateAPI() const { return _pExchangePrivate != nullptr; }

bool healthCheck() { return _exchangePublic.healthCheck(); }

Expand Down
14 changes: 7 additions & 7 deletions src/engine/include/coincenter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,15 @@ class Coincenter {
/// If no exchange name is given, it will attempt to trade given amount on all exchanges with the sufficient balance.
/// If exactly one private exchange is given, balance will not be queried and trade will be launched without balance
/// check.
TradedAmountsPerExchange trade(MonetaryAmount startAmount, bool isPercentageTrade, CurrencyCode toCurrency,
std::span<const ExchangeName> privateExchangeNames, const TradeOptions &tradeOptions);
TradeResultPerExchange trade(MonetaryAmount startAmount, bool isPercentageTrade, CurrencyCode toCurrency,
std::span<const ExchangeName> privateExchangeNames, const TradeOptions &tradeOptions);

TradedAmountsPerExchange smartBuy(MonetaryAmount endAmount, std::span<const ExchangeName> privateExchangeNames,
const TradeOptions &tradeOptions);
TradeResultPerExchange smartBuy(MonetaryAmount endAmount, std::span<const ExchangeName> privateExchangeNames,
const TradeOptions &tradeOptions);

TradedAmountsPerExchange smartSell(MonetaryAmount startAmount, bool isPercentageTrade,
std::span<const ExchangeName> privateExchangeNames,
const TradeOptions &tradeOptions);
TradeResultPerExchange smartSell(MonetaryAmount startAmount, bool isPercentageTrade,
std::span<const ExchangeName> privateExchangeNames,
const TradeOptions &tradeOptions);

/// Single withdraw of 'grossAmount' from 'fromExchangeName' to 'toExchangeName'
DeliveredWithdrawInfoWithExchanges withdraw(MonetaryAmount grossAmount, bool isPercentageWithdraw,
Expand Down
Loading

0 comments on commit 25e9fc6

Please sign in to comment.