Skip to content

Commit

Permalink
Correctly take into account depth parameter for Binance & Kucoin orde…
Browse files Browse the repository at this point in the history
…r book queries
  • Loading branch information
sjanel committed Mar 24, 2024
1 parent af71cd7 commit daaaf07
Show file tree
Hide file tree
Showing 15 changed files with 83 additions and 81 deletions.
2 changes: 1 addition & 1 deletion src/api/exchanges/include/huobipublicapi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class HuobiPublic : public ExchangePublic {
return _allOrderBooksCache.get(depth);
}

MarketOrderBook queryOrderBook(Market mk, int depth = kHuobiStandardOrderBookDefaultDepth) override {
MarketOrderBook queryOrderBook(Market mk, int depth = kDefaultDepth) override {
return _orderbookCache.get(mk, depth);
}

Expand Down
4 changes: 3 additions & 1 deletion src/api/exchanges/include/kucoinpublicapi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ class KucoinPublic : public ExchangePublic {
return _allOrderBooksCache.get(depth);
}

MarketOrderBook queryOrderBook(Market mk, int depth = 10) override { return _orderbookCache.get(mk, depth); }
MarketOrderBook queryOrderBook(Market mk, int depth = kDefaultDepth) override {
return _orderbookCache.get(mk, depth);
}

MonetaryAmount queryLast24hVolume(Market mk) override { return _tradedVolumeCache.get(mk); }

Expand Down
6 changes: 4 additions & 2 deletions src/api/exchanges/src/binancepublicapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <iterator>
#include <memory>
#include <optional>
#include <ranges>
#include <string_view>
#include <unordered_map>
#include <utility>
Expand Down Expand Up @@ -497,10 +498,11 @@ MarketOrderBook BinancePublic::OrderBookFunc::operator()(Market mk, int depth) {
const auto bidsIt = asksAndBids.find("bids");

if (asksIt != asksAndBids.end() && bidsIt != asksAndBids.end()) {
orderBookLines.reserve(asksIt->size() + bidsIt->size());
orderBookLines.reserve(std::min(static_cast<decltype(depth)>(asksIt->size()), depth) +
std::min(static_cast<decltype(depth)>(bidsIt->size()), depth));
for (const auto& asksOrBids : {asksIt, bidsIt}) {
const auto type = asksOrBids == asksIt ? OrderBookLine::Type::kAsk : OrderBookLine::Type::kBid;
for (const auto& priceQuantityPair : *asksOrBids) {
for (const auto& priceQuantityPair : *asksOrBids | std::ranges::views::take(depth)) {
MonetaryAmount amount(priceQuantityPair.back().get<std::string_view>(), mk.base());
MonetaryAmount price(priceQuantityPair.front().get<std::string_view>(), mk.quote());

Expand Down
18 changes: 7 additions & 11 deletions src/api/exchanges/src/huobipublicapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <array>
#include <cstdint>
#include <optional>
#include <ranges>
#include <string_view>
#include <unordered_map>
#include <utility>
Expand Down Expand Up @@ -373,36 +374,31 @@ MarketOrderBook HuobiPublic::OrderBookFunc::operator()(Market mk, int depth) {
CurlPostData postData{{"symbol", mk.assetsPairStrLower()}, {"type", "step0"}};
if (depth != kHuobiStandardOrderBookDefaultDepth) {
static constexpr std::array kAuthorizedDepths = {5, 10, 20, kHuobiStandardOrderBookDefaultDepth};
auto lb = std::ranges::lower_bound(kAuthorizedDepths, depth);
const auto lb = std::ranges::lower_bound(kAuthorizedDepths, depth);
if (lb == kAuthorizedDepths.end()) {
log::warn("Invalid depth {}, default to {}", depth, kHuobiStandardOrderBookDefaultDepth);
} else if (*lb != kHuobiStandardOrderBookDefaultDepth) {
postData.append("depth", *lb);
}
}

MarketOrderBookLines orderBookLines;

const json asksAndBids = PublicQuery(_curlHandle, "/market/depth", postData);
const auto nowTime = Clock::now();
const auto asksIt = asksAndBids.find("asks");
const auto bidsIt = asksAndBids.find("bids");

if (asksIt != asksAndBids.end() && bidsIt != asksAndBids.end()) {
orderBookLines.reserve(bidsIt->size() + asksIt->size());
orderBookLines.reserve(std::min(static_cast<decltype(depth)>(asksIt->size()), depth) +
std::min(static_cast<decltype(depth)>(bidsIt->size()), depth));
for (const auto& asksOrBids : {bidsIt, asksIt}) {
int currentDepth = 0;
const auto type = asksOrBids == asksIt ? OrderBookLine::Type::kAsk : OrderBookLine::Type::kBid;
for (const auto& priceQuantityPair : *asksOrBids) {
for (const auto& priceQuantityPair : *asksOrBids | std::ranges::views::take(depth)) {
MonetaryAmount amount(priceQuantityPair.back().get<double>(), mk.base());
MonetaryAmount price(priceQuantityPair.front().get<double>(), mk.quote());

orderBookLines.push(amount, price, type);
if (++currentDepth == depth) {
if (depth < static_cast<int>(asksOrBids->size())) {
log::debug("Truncate number of {} prices in order book to {}",
type == OrderBookLine::Type::kAsk ? "ask" : "bid", depth);
}
break;
}
}
}
}
Expand Down
41 changes: 17 additions & 24 deletions src/api/exchanges/src/kucoinpublicapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <cstdint>
#include <iterator>
#include <optional>
#include <ranges>
#include <string_view>
#include <utility>

Expand Down Expand Up @@ -261,27 +262,6 @@ MarketOrderBookMap KucoinPublic::AllOrderBooksFunc::operator()(int depth) {
return ret;
}

namespace {
template <class InputIt>
void FillOrderBook(Market mk, int depth, OrderBookLine::Type type, InputIt beg, InputIt end,
MarketOrderBookLines& orderBookLines) {
int currentDepth = 0;
for (auto it = beg; it != end; ++it) {
MonetaryAmount price((*it)[0].template get<std::string_view>(), mk.quote());
MonetaryAmount amount((*it)[1].template get<std::string_view>(), mk.base());

orderBookLines.push(amount, price, type);
if (++currentDepth == depth) {
if (++it != end) {
log::debug("Truncate number of {} prices in order book to {}",
type == OrderBookLine::Type::kAsk ? "ask" : "bid", depth);
}
break;
}
}
}
} // namespace

MarketOrderBook KucoinPublic::OrderBookFunc::operator()(Market mk, int depth) {
// Kucoin has a fixed range of authorized values for depth
static constexpr std::array kAuthorizedDepths = {20, 100};
Expand All @@ -301,10 +281,23 @@ MarketOrderBook KucoinPublic::OrderBookFunc::operator()(Market mk, int depth) {
const auto asksIt = asksAndBids.find("asks");
const auto bidsIt = asksAndBids.find("bids");
if (asksIt != asksAndBids.end() && bidsIt != asksAndBids.end()) {
orderBookLines.reserve(asksIt->size() + bidsIt->size());
orderBookLines.reserve(std::min(static_cast<decltype(depth)>(asksIt->size()), depth) +
std::min(static_cast<decltype(depth)>(bidsIt->size()), depth));

// Reverse iterate as bids are received in descending order
FillOrderBook(mk, depth, OrderBookLine::Type::kBid, bidsIt->rbegin(), bidsIt->rend(), orderBookLines);
FillOrderBook(mk, depth, OrderBookLine::Type::kAsk, asksIt->begin(), asksIt->end(), orderBookLines);
for (const auto& val : *bidsIt | std::views::reverse | std::ranges::views::take(depth)) {
MonetaryAmount price(val[0].get<std::string_view>(), mk.quote());
MonetaryAmount amount(val[1].get<std::string_view>(), mk.base());

orderBookLines.pushBid(amount, price);
}

for (const auto& val : *asksIt | std::ranges::views::take(depth)) {
MonetaryAmount price(val[0].get<std::string_view>(), mk.quote());
MonetaryAmount amount(val[1].get<std::string_view>(), mk.base());

orderBookLines.pushAsk(amount, price);
}
}

return MarketOrderBook(nowTime, mk, orderBookLines);
Expand Down
8 changes: 2 additions & 6 deletions src/api/exchanges/src/upbitpublicapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <cstddef>
#include <cstdint>
#include <optional>
#include <ranges>
#include <string_view>
#include <utility>

Expand Down Expand Up @@ -202,7 +203,7 @@ MarketOrderBookMap ParseOrderBooks(const json& result, int depth) {

orderBookLines.reserve(orderBookLinesJson.size() * 2U);

for (const json& orderbookDetails : orderBookLinesJson) {
for (const json& orderbookDetails : orderBookLinesJson | std::ranges::views::take(depth)) {
// Amounts are not strings, but doubles
MonetaryAmount askPri(orderbookDetails["ask_price"].get<double>(), quote);
MonetaryAmount bidPri(orderbookDetails["bid_price"].get<double>(), quote);
Expand All @@ -211,11 +212,6 @@ MarketOrderBookMap ParseOrderBooks(const json& result, int depth) {

orderBookLines.pushAsk(askVol, askPri);
orderBookLines.pushBid(bidVol, bidPri);

if (static_cast<int>(orderBookLines.size() / 2) == depth) {
// Upbit does not have a depth parameter, the only thing we can do is to truncate it manually
break;
}
}
if (static_cast<int>(orderBookLines.size() / 2) < depth) {
log::warn("Upbit does not support orderbook depth larger than {}", orderBookLines.size() / 2);
Expand Down
8 changes: 5 additions & 3 deletions src/engine/include/coincentercommand.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class CoincenterCommand {

CoincenterCommand& setAmount(MonetaryAmount amount);

CoincenterCommand& setDepth(int depth);
CoincenterCommand& setDepth(int32_t depth);

CoincenterCommand& setMarket(Market market);

Expand Down Expand Up @@ -76,8 +76,10 @@ class CoincenterCommand {

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

using trivially_relocatable = std::bool_constant<is_trivially_relocatable_v<ExchangeNames> &&
is_trivially_relocatable_v<OrdersConstraints>>::type;
using trivially_relocatable =
std::bool_constant<is_trivially_relocatable_v<ExchangeNames> && is_trivially_relocatable_v<OrdersConstraints> &&
is_trivially_relocatable_v<WithdrawsOrDepositsConstraints> &&
is_trivially_relocatable_v<TradeOptions> && is_trivially_relocatable_v<WithdrawOptions>>::type;

private:
using SpecialOptions =
Expand Down
9 changes: 6 additions & 3 deletions src/engine/include/coincenteroptions.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <cstdint>
#include <optional>
#include <ostream>
#include <string_view>
Expand All @@ -18,6 +19,8 @@ namespace cct {

class CoincenterCmdLineOptions {
public:
static constexpr int32_t kUndefinedDepth = std::numeric_limits<int32_t>::min();

static std::ostream& PrintVersion(std::string_view programName, std::ostream& os) noexcept;

constexpr CoincenterCmdLineOptions() noexcept = default;
Expand Down Expand Up @@ -97,9 +100,9 @@ class CoincenterCmdLineOptions {

std::string_view lastTrades;

CommandLineOptionalInt repeats;
int monitoringPort = CoincenterCmdLineOptionsDefinitions::kDefaultMonitoringPort;
int depth = 0;
CommandLineOptionalInt32 repeats;
int32_t monitoringPort = CoincenterCmdLineOptionsDefinitions::kDefaultMonitoringPort;
int32_t depth = kUndefinedDepth;

bool forceMultiTrade = false;
bool forceSingleTrade = false;
Expand Down
2 changes: 1 addition & 1 deletion src/engine/include/coincenteroptionsdef.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ namespace cct {
class CoincenterCmdLineOptionsDefinitions {
public:
static constexpr std::string_view kDefaultMonitoringIPAddress = "0.0.0.0"; // in Docker, localhost does not work
static constexpr int kDefaultMonitoringPort = 9091; // Prometheus default push port
static constexpr uint16_t kDefaultMonitoringPort = 9091; // Prometheus default push port
static constexpr Duration kDefaultRepeatTime = seconds(1);

static constexpr int64_t kDefaultRepeatDurationSeconds =
Expand Down
19 changes: 10 additions & 9 deletions src/engine/include/commandlineoption.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <compare>
#include <cstdint>
#include <optional>
#include <string_view>
#include <variant>
Expand Down Expand Up @@ -70,34 +71,34 @@ class CommandLineOption {

/// Basically an extension of the std::optional class with an additional state.
/// Indeed, we want to distinguish the presence of the option with its optional value.
class CommandLineOptionalInt {
class CommandLineOptionalInt32 {
public:
enum class State : int8_t { kValueIsSet, kOptionPresent, kOptionNotPresent };

constexpr CommandLineOptionalInt() noexcept = default;
constexpr CommandLineOptionalInt32() noexcept = default;

constexpr CommandLineOptionalInt(State state) : _state(state) {}
constexpr CommandLineOptionalInt32(State state) : _state(state) {}

constexpr CommandLineOptionalInt(int value) : _value(value), _state(State::kValueIsSet) {}
constexpr CommandLineOptionalInt32(int32_t value) : _value(value), _state(State::kValueIsSet) {}

constexpr int& operator*() { return _value; }
constexpr int operator*() const { return _value; }
constexpr auto& operator*() { return _value; }
constexpr auto operator*() const { return _value; }

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;
bool operator==(const CommandLineOptionalInt32&) const noexcept = default;

private:
int _value = 0;
int32_t _value = 0;
State _state = State::kOptionNotPresent;
};

template <class OptValueType>
struct AllowedCommandLineOptionsBase {
using CommandLineOptionType =
std::variant<std::string_view OptValueType::*, std::optional<std::string_view> OptValueType::*,
int OptValueType::*, CommandLineOptionalInt OptValueType::*, bool OptValueType::*,
int OptValueType::*, CommandLineOptionalInt32 OptValueType::*, bool OptValueType::*,
Duration OptValueType::*>;
using CommandLineOptionWithValue = std::pair<CommandLineOption, CommandLineOptionType>;
};
Expand Down
8 changes: 4 additions & 4 deletions src/engine/include/commandlineoptionsparser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,13 @@ class CommandLineOptionsParser {
}
},

// CommandLineOptionalInt value matcher
[&data, &idx, argv](CommandLineOptionalInt OptValueType::*arg) {
data.*arg = CommandLineOptionalInt(CommandLineOptionalInt::State::kOptionPresent);
// CommandLineOptionalInt32 value matcher
[&data, &idx, argv](CommandLineOptionalInt32 OptValueType::*arg) {
data.*arg = CommandLineOptionalInt32(CommandLineOptionalInt32::State::kOptionPresent);
if (idx + 1U < argv.size()) {
std::string_view opt(argv[idx + 1]);
if (IsOptionInt(opt)) {
data.*arg = FromString<int>(opt);
data.*arg = FromString<int32_t>(opt);
++idx;
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/engine/src/coincentercommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ CoincenterCommand& CoincenterCommand::setAmount(MonetaryAmount amount) {
return *this;
}

CoincenterCommand& CoincenterCommand::setDepth(int depth) {
CoincenterCommand& CoincenterCommand::setDepth(int32_t depth) {
if (depth < 0) {
throw exception("Depth cannot be negative");
}
_n = depth;
return *this;
}
Expand Down
22 changes: 13 additions & 9 deletions src/engine/src/coincentercommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,13 @@ void CoincenterCommands::addOption(const CoincenterCmdLineOptions &cmdLineOption

if (!cmdLineOptions.orderbook.empty()) {
optionParser = StringOptionParser(cmdLineOptions.orderbook);
_commands.emplace_back(CoincenterCommandType::kOrderbook)
.setMarket(optionParser.parseMarket())
.setExchangeNames(optionParser.parseExchanges())
.setDepth(cmdLineOptions.depth)
.setCur1(cmdLineOptions.orderbookCur);
auto &cmd = _commands.emplace_back(CoincenterCommandType::kOrderbook)
.setMarket(optionParser.parseMarket())
.setExchangeNames(optionParser.parseExchanges())
.setCur1(cmdLineOptions.orderbookCur);
if (cmdLineOptions.depth != CoincenterCmdLineOptions::kUndefinedDepth) {
cmd.setDepth(cmdLineOptions.depth);
}
}

if (cmdLineOptions.ticker) {
Expand Down Expand Up @@ -182,10 +184,12 @@ void CoincenterCommands::addOption(const CoincenterCmdLineOptions &cmdLineOption

if (!cmdLineOptions.lastTrades.empty()) {
optionParser = StringOptionParser(cmdLineOptions.lastTrades);
_commands.emplace_back(CoincenterCommandType::kLastTrades)
.setMarket(optionParser.parseMarket())
.setDepth(cmdLineOptions.depth)
.setExchangeNames(optionParser.parseExchanges());
auto &cmd = _commands.emplace_back(CoincenterCommandType::kLastTrades)
.setMarket(optionParser.parseMarket())
.setExchangeNames(optionParser.parseExchanges());
if (cmdLineOptions.depth != CoincenterCmdLineOptions::kUndefinedDepth) {
cmd.setDepth(cmdLineOptions.depth);
}
}

if (!cmdLineOptions.lastPrice.empty()) {
Expand Down
2 changes: 1 addition & 1 deletion src/engine/src/exchangesorchestrator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ MarketOrderBookConversionRates ExchangesOrchestrator::getMarketOrderBooks(Market
log::warn("Unable to convert {} into {} on {}", mk.quote(), equiCurrencyCode, exchange->name());
}
return std::make_tuple(exchange->name(),
depth ? exchange->queryOrderBook(mk, *depth) : exchange->queryOrderBook(mk),
exchange->queryOrderBook(mk, depth.value_or(api::ExchangePublic::kDefaultDepth)),
optConversionRate);
};
_threadPool.parallelTransform(selectedExchanges.begin(), selectedExchanges.end(), ret.begin(), marketOrderBooksFunc);
Expand Down
Loading

0 comments on commit daaaf07

Please sign in to comment.