diff --git a/.dockerignore b/.dockerignore index d07c51f7..d8d4c498 100644 --- a/.dockerignore +++ b/.dockerignore @@ -9,6 +9,7 @@ LICENSE **/*secret.json data/log data/secret +data/serialized !data/secret/secret_test.json monitoring resources diff --git a/.github/workflows/ubuntu-monitoring.yml b/.github/workflows/ubuntu-special.yml similarity index 85% rename from .github/workflows/ubuntu-monitoring.yml rename to .github/workflows/ubuntu-special.yml index a0dc9091..d7554cff 100644 --- a/.github/workflows/ubuntu-monitoring.yml +++ b/.github/workflows/ubuntu-special.yml @@ -1,4 +1,4 @@ -name: Monitoring +name: Special on: push: @@ -7,14 +7,14 @@ on: pull_request: jobs: - ubuntu-monitoring-build: - name: Build on Ubuntu with monitoring support + ubuntu-special-build: + name: Build on Ubuntu with monitoring / protobuf support runs-on: ubuntu-latest strategy: matrix: compiler: [g++-11] buildmode: [Debug] - build-prometheus-from-source: [0, 1] + build-special-from-source: [0, 1] steps: - name: Checkout repository code @@ -38,7 +38,7 @@ jobs: ninja sudo cmake --install . - if: matrix.build-prometheus-from-source == 0 + if: matrix.build-special-from-source == 0 - name: Create Build Environment run: cmake -E make_directory ${{github.workspace}}/build @@ -46,7 +46,7 @@ jobs: - name: Configure CMake working-directory: ${{github.workspace}}/build shell: bash - run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{matrix.buildmode}} -DCMAKE_CXX_COMPILER=${{matrix.compiler}} -DCCT_BUILD_PROMETHEUS_FROM_SRC=${{matrix.build-prometheus-from-source}} -GNinja + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{matrix.buildmode}} -DCMAKE_CXX_COMPILER=${{matrix.compiler}} -DCCT_BUILD_PROMETHEUS_FROM_SRC=${{matrix.build-special-from-source}} -DCCT_ENABLE_PROTO=${{matrix.build-special-from-source}} -GNinja - name: Build working-directory: ${{github.workspace}}/build diff --git a/.gitignore b/.gitignore index 8c01a78d..0e8592b9 100644 --- a/.gitignore +++ b/.gitignore @@ -66,6 +66,7 @@ cscope* data/cache data/log data/secret +data/serialized data/static/exchangeconfig.json data/static/generalconfig.json monitoring/data/grafana/* diff --git a/CMakeLists.txt b/CMakeLists.txt index dc63a7a1..658e11a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ option(CCT_ENABLE_TESTS "Build the unit tests" ${MAIN_PROJECT}) option(CCT_BUILD_EXEC "Build an executable instead of a static library" ${MAIN_PROJECT}) option(CCT_ENABLE_ASAN "Compile with AddressSanitizer" ${CCT_ASAN_BUILD}) option(CCT_ENABLE_CLANG_TIDY "Compile with clang-tidy checks" OFF) +option(CCT_ENABLE_PROTO "Compile with protobuf support (to export data to the outside world)" ON) option(CCT_BUILD_PROMETHEUS_FROM_SRC "Fetch and build from prometheus-cpp sources" OFF) set(CCT_DATA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/data" CACHE PATH "Needed data directory for coincenter. Can also be overriden at runtime with this environment variable") @@ -135,6 +136,34 @@ else() endif() endif() +if(CCT_ENABLE_PROTO) + find_package(Protobuf CONFIG) + if(protobuf_FOUND) + message(STATUS "Linking with protobuf ${protobuf_VERSION}") + else() + set(PROTOBUF_VERSION v25.3) + if (MSVC) + protobuf v25 does not compile with MSVC: https://github.com/protocolbuffers/protobuf/issues/14602 + set(PROTOBUF_VERSION v26.0-rc2) + endif() + + message(STATUS "Compiling protobuf ${PROTOBUF_VERSION} from sources") + + set(protobuf_BUILD_TESTS OFF) + set(ABSL_PROPAGATE_CXX_STD ON) + + FetchContent_Declare( + protobuf + GIT_REPOSITORY https://github.com/protocolbuffers/protobuf.git + GIT_TAG ${PROTOBUF_VERSION} + ) + FetchContent_MakeAvailable(protobuf) + + include(${protobuf_SOURCE_DIR}/cmake/protobuf-generate.cmake) + + endif() +endif() + # Unit Tests #[[ Create an executable @@ -250,13 +279,20 @@ if(CCT_ENABLE_PROMETHEUS) add_compile_definitions(CCT_ENABLE_PROMETHEUS) endif() +if(CCT_ENABLE_PROTO) + add_compile_definitions(CCT_ENABLE_PROTO) + add_compile_definitions("CCT_PROTOBUF_VERSION=\"${PROTOBUF_VERSION}\"") +endif() + # Link to sub folders CMakeLists.txt, from the lowest level to the highest level for documentation # (beware of cyclic dependencies) add_subdirectory(src/tech) add_subdirectory(src/monitoring) add_subdirectory(src/http-request) add_subdirectory(src/objects) +add_subdirectory(src/serialization) add_subdirectory(src/api-objects) +add_subdirectory(src/trading) add_subdirectory(src/api) add_subdirectory(src/engine) add_subdirectory(src/main) diff --git a/CONFIG.md b/CONFIG.md index efba43e0..b2c031c0 100644 --- a/CONFIG.md +++ b/CONFIG.md @@ -150,6 +150,7 @@ Refer to the hardcoded default json example as a model in case of doubt. | *query* | **updateFrequency.depositWallet** | Duration string (ex: `1min`) | Minimum duration between two consecutive requests of deposit information (including wallet) | | *query* | **updateFrequency.currencyInfo** | Duration string (ex: `4h`) | Minimum duration between two consecutive requests of dynamic currency info retrieval on Bithumb only (used for place order) | | *query* | **placeSimulateRealOrder** | Boolean (`true` or `false`) | If `true`, in trade simulation mode (with `--sim`) exchanges which do not support simulated mode in place order will actually place a real order, with the following characteristics: This will allow place of a 'real' order that cannot be matched in practice (if it is, lucky you!) | +| *query* | **marketDataSerialization** | Boolean (`true` or `false`) | If `true` and `coincenter` is compiled with **protobuf** support, some market data will automatically be exported in the `data/serialization` directory (`orderbook` and `last-trades`) for a long term storage | | *query* | **multiTradeAllowedByDefault** | Boolean (`true` or `false`) | If `true`, [multi-trade](README.md#multi-trade) will be allowed by default for `trade`, `buy` and `sell`. It can be overridden at command line level with `--no-multi-trade` and `--multi-trade`. | | *query* | **validateApiKey** | Boolean (`true` or `false`) | If `true`, each loaded private key will be tested at start of the program. In case of a failure, it will be removed from the list of private accounts loaded by `coincenter`, so that later queries do not consider it instead of raising a runtime exception. The downside is that it will make an additional check that will make startup slower. | | | *tradefees* | **maker** | String as decimal number representing a percentage (for instance, "0.15") | Trade fees occurring when a maker order is matched | diff --git a/Dockerfile b/Dockerfile index 73202ad0..7aee1bbe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ FROM ubuntu:22.04 AS build # Install base & build dependencies, needed certificates for curl to work with https RUN apt update && \ apt upgrade -y && \ - apt install build-essential curl libcurl4-gnutls-dev libssl-dev cmake git ca-certificates gzip -y && \ + apt install build-essential curl libcurl4-gnutls-dev libssl-dev cmake git ca-certificates gzip --no-install-recommends -y && \ curl -L -o /usr/local/bin/ninja.gz https://github.com/ninja-build/ninja/releases/latest/download/ninja-linux.zip && \ gunzip /usr/local/bin/ninja.gz && \ chmod a+x /usr/local/bin/ninja @@ -23,12 +23,14 @@ ARG BUILD_MODE=Release ARG BUILD_TEST=0 ARG BUILD_ASAN=0 ARG BUILD_WITH_PROMETHEUS=1 +ARG BUILD_WITH_PROTOBUF=1 # Build and launch tests if any RUN cmake -DCMAKE_BUILD_TYPE=${BUILD_MODE} \ -DCCT_ENABLE_TESTS=${BUILD_TEST} \ -DCCT_ENABLE_ASAN=${BUILD_ASAN} \ -DCCT_BUILD_PROMETHEUS_FROM_SRC=${BUILD_WITH_PROMETHEUS} \ + -DCCT_ENABLE_PROTO=${BUILD_WITH_PROTOBUF} \ -GNinja .. && \ ninja && \ if [ "$BUILD_TEST" = "1" -o "$BUILD_TEST" = "ON" ]; then \ @@ -56,4 +58,4 @@ COPY --from=build /app/bin/coincenter /app/coincenter # 'data' directory of host machine can be mounted when launching the container. # To do this, you can use --mount option: # docker run --mount type=bind,source=,target=/app/data sjanel/coincenter -ENTRYPOINT [ "/app/coincenter" ] \ No newline at end of file +ENTRYPOINT [ "/app/coincenter" ] diff --git a/alpine.Dockerfile b/alpine.Dockerfile index b52cab0b..da56099f 100644 --- a/alpine.Dockerfile +++ b/alpine.Dockerfile @@ -2,7 +2,7 @@ FROM alpine:3.19.1 AS build # Install base & build dependencies, needed certificates for curl to work with https -RUN apk add --update --upgrade --no-cache g++ libc-dev curl-dev cmake ninja git ca-certificates +RUN apk add --update --upgrade --no-cache g++ linux-headers libc-dev curl-dev cmake ninja git ca-certificates # Set default directory for application WORKDIR /app @@ -18,12 +18,14 @@ ARG BUILD_MODE=Release ARG BUILD_TEST=0 ARG BUILD_ASAN=0 ARG BUILD_WITH_PROMETHEUS=1 +ARG BUILD_WITH_PROTOBUF=1 # Build and launch tests if any RUN cmake -DCMAKE_BUILD_TYPE=${BUILD_MODE} \ -DCCT_ENABLE_TESTS=${BUILD_TEST} \ -DCCT_ENABLE_ASAN=${BUILD_ASAN} \ -DCCT_BUILD_PROMETHEUS_FROM_SRC=${BUILD_WITH_PROMETHEUS} \ + -DCCT_ENABLE_PROTO=${BUILD_WITH_PROTOBUF} \ -GNinja .. && \ ninja && \ if [ "$BUILD_TEST" = "1" -o "$BUILD_TEST" = "ON" ]; then \ diff --git a/src/api-objects/include/closed-order.hpp b/src/api-objects/include/closed-order.hpp new file mode 100644 index 00000000..eeddb7c7 --- /dev/null +++ b/src/api-objects/include/closed-order.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +#include "cct_string.hpp" +#include "cct_type_traits.hpp" +#include "market.hpp" +#include "monetaryamount.hpp" +#include "order.hpp" +#include "orderid.hpp" +#include "timedef.hpp" +#include "tradeside.hpp" + +namespace cct { + +class ClosedOrder : public Order { + public: + ClosedOrder(OrderId id, MonetaryAmount matchedVolume, MonetaryAmount price, TimePoint placedTime, + TimePoint matchedTime, TradeSide side); + + TimePoint matchedTime() const { return _matchedTime; } + + string matchedTimeStr() const; + + private: + TimePoint _matchedTime; +}; +} // namespace cct \ No newline at end of file diff --git a/src/api-objects/include/exchangeprivateapitypes.hpp b/src/api-objects/include/exchangeprivateapitypes.hpp index 3351fd76..a41c6997 100644 --- a/src/api-objects/include/exchangeprivateapitypes.hpp +++ b/src/api-objects/include/exchangeprivateapitypes.hpp @@ -2,8 +2,9 @@ #include "cct_flatset.hpp" #include "cct_vector.hpp" +#include "closed-order.hpp" #include "deposit.hpp" -#include "order.hpp" +#include "opened-order.hpp" #include "tradedamounts.hpp" #include "withdraw.hpp" @@ -14,8 +15,10 @@ using DepositsSet = FlatSet; using Withdraws = vector; using WithdrawsSet = FlatSet; -using Orders = vector; -using OrdersSet = FlatSet; +using OpenedOrderVector = vector; +using OpenedOrderSet = FlatSet; + +using ClosedOrders = vector; using TradedAmountsVector = vector; diff --git a/src/api-objects/include/exchangepublicapitypes.hpp b/src/api-objects/include/exchangepublicapitypes.hpp index f1e28e2f..7b8d8eec 100644 --- a/src/api-objects/include/exchangepublicapitypes.hpp +++ b/src/api-objects/include/exchangepublicapitypes.hpp @@ -12,9 +12,12 @@ #include "publictrade.hpp" namespace cct { + using MarketSet = FlatSet; using MarketOrderBookMap = std::unordered_map; using MarketPriceMap = std::unordered_map; using MarketsPath = SmallVector; using TradesVector = vector; -} // namespace cct \ No newline at end of file +using MarketOrderBookVector = vector; + +} // namespace cct diff --git a/src/api-objects/include/opened-order.hpp b/src/api-objects/include/opened-order.hpp new file mode 100644 index 00000000..72536216 --- /dev/null +++ b/src/api-objects/include/opened-order.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "monetaryamount.hpp" +#include "order.hpp" +#include "orderid.hpp" +#include "timedef.hpp" +#include "tradeside.hpp" + +namespace cct { + +class OpenedOrder : public Order { + public: + OpenedOrder(OrderId id, MonetaryAmount matchedVolume, MonetaryAmount remainingVolume, MonetaryAmount price, + TimePoint placedTime, TradeSide side); + + MonetaryAmount originalVolume() const { return matchedVolume() + _remainingVolume; } + MonetaryAmount remainingVolume() const { return _remainingVolume; } + + private: + MonetaryAmount _remainingVolume; +}; +} // namespace cct \ No newline at end of file diff --git a/src/api-objects/include/order.hpp b/src/api-objects/include/order.hpp index 2510148c..108b8f37 100644 --- a/src/api-objects/include/order.hpp +++ b/src/api-objects/include/order.hpp @@ -15,25 +15,13 @@ namespace cct { class Order { public: - Order(const char *id, MonetaryAmount matchedVolume, MonetaryAmount remainingVolume, MonetaryAmount price, - TimePoint placedTime, TradeSide side) - : Order(OrderId(id), matchedVolume, remainingVolume, price, placedTime, side) {} - - Order(std::string_view id, MonetaryAmount matchedVolume, MonetaryAmount remainingVolume, MonetaryAmount price, - TimePoint placedTime, TradeSide side); - - Order(OrderId &&id, MonetaryAmount matchedVolume, MonetaryAmount remainingVolume, MonetaryAmount price, - TimePoint placedTime, TradeSide side); - - MonetaryAmount originalVolume() const { return _matchedVolume + _remainingVolume; } - MonetaryAmount matchedVolume() const { return _matchedVolume; } - MonetaryAmount remainingVolume() const { return _remainingVolume; } - MonetaryAmount price() const { return _price; } + TimePoint placedTime() const { return _placedTime; } OrderId &id() { return _id; } const OrderId &id() const { return _id; } - TimePoint placedTime() const { return _placedTime; } + MonetaryAmount matchedVolume() const { return _matchedVolume; } + MonetaryAmount price() const { return _price; } TradeSide side() const { return _side; } @@ -47,11 +35,13 @@ class Order { using trivially_relocatable = is_trivially_relocatable::type; + protected: + Order(OrderId id, MonetaryAmount matchedVolume, MonetaryAmount price, TimePoint placedTime, TradeSide side); + private: TimePoint _placedTime; OrderId _id; // exchange internal id, format specific to each exchange MonetaryAmount _matchedVolume; - MonetaryAmount _remainingVolume; MonetaryAmount _price; TradeSide _side; }; diff --git a/src/api-objects/src/closed-order.cpp b/src/api-objects/src/closed-order.cpp new file mode 100644 index 00000000..3b3b0344 --- /dev/null +++ b/src/api-objects/src/closed-order.cpp @@ -0,0 +1,19 @@ +#include "closed-order.hpp" + +#include + +#include "cct_string.hpp" +#include "monetaryamount.hpp" +#include "order.hpp" +#include "timedef.hpp" +#include "timestring.hpp" +#include "tradeside.hpp" + +namespace cct { + +ClosedOrder::ClosedOrder(OrderId id, MonetaryAmount matchedVolume, MonetaryAmount price, TimePoint placedTime, + TimePoint matchedTime, TradeSide side) + : Order(std::move(id), matchedVolume, price, placedTime, side), _matchedTime(matchedTime) {} + +string ClosedOrder::matchedTimeStr() const { return ToString(_matchedTime); } +} // namespace cct \ No newline at end of file diff --git a/src/api-objects/src/opened-order.cpp b/src/api-objects/src/opened-order.cpp new file mode 100644 index 00000000..aa7ea5aa --- /dev/null +++ b/src/api-objects/src/opened-order.cpp @@ -0,0 +1,11 @@ +#include "opened-order.hpp" + +#include + +namespace cct { + +OpenedOrder::OpenedOrder(string id, MonetaryAmount matchedVolume, MonetaryAmount remainingVolume, MonetaryAmount price, + TimePoint placedTime, TradeSide side) + : Order(std::move(id), matchedVolume, price, placedTime, side), _remainingVolume(remainingVolume) {} + +} // namespace cct \ No newline at end of file diff --git a/src/api-objects/src/order.cpp b/src/api-objects/src/order.cpp index 6f412a3c..49fcd6c3 100644 --- a/src/api-objects/src/order.cpp +++ b/src/api-objects/src/order.cpp @@ -11,23 +11,8 @@ namespace cct { -Order::Order(std::string_view id, MonetaryAmount matchedVolume, MonetaryAmount remainingVolume, MonetaryAmount price, - TimePoint placedTime, TradeSide side) - : _placedTime(placedTime), - _id(id), - _matchedVolume(matchedVolume), - _remainingVolume(remainingVolume), - _price(price), - _side(side) {} - -Order::Order(string &&id, MonetaryAmount matchedVolume, MonetaryAmount remainingVolume, MonetaryAmount price, - TimePoint placedTime, TradeSide side) - : _placedTime(placedTime), - _id(std::move(id)), - _matchedVolume(matchedVolume), - _remainingVolume(remainingVolume), - _price(price), - _side(side) {} +Order::Order(string id, MonetaryAmount matchedVolume, MonetaryAmount price, TimePoint placedTime, TradeSide side) + : _placedTime(placedTime), _id(std::move(id)), _matchedVolume(matchedVolume), _price(price), _side(side) {} std::string_view Order::sideStr() const { return SideStr(_side); } diff --git a/src/api/common/include/exchangeprivateapi.hpp b/src/api/common/include/exchangeprivateapi.hpp index b465c7f0..afd53af1 100644 --- a/src/api/common/include/exchangeprivateapi.hpp +++ b/src/api/common/include/exchangeprivateapi.hpp @@ -61,7 +61,8 @@ class ExchangePrivate : public ExchangeBase { virtual bool canGenerateDepositAddress() const = 0; /// Get opened orders filtered according to given constraints - virtual Orders queryOpenedOrders(const OrdersConstraints &openedOrdersConstraints = OrdersConstraints()) = 0; + virtual OpenedOrderVector queryOpenedOrders( + const OrdersConstraints &openedOrdersConstraints = OrdersConstraints()) = 0; /// Cancel all opened orders on the exchange that matches given constraints /// @return number of opened orders cancelled diff --git a/src/api/common/include/exchangeprivateapi_mock.hpp b/src/api/common/include/exchangeprivateapi_mock.hpp index 2377e768..95a9d013 100644 --- a/src/api/common/include/exchangeprivateapi_mock.hpp +++ b/src/api/common/include/exchangeprivateapi_mock.hpp @@ -27,7 +27,7 @@ class MockExchangePrivate : public ExchangePrivate { MOCK_METHOD(BalancePortfolio, queryAccountBalance, (const BalanceOptions &), (override)); MOCK_METHOD(Wallet, queryDepositWallet, (CurrencyCode), (override)); MOCK_METHOD(bool, canGenerateDepositAddress, (), (const override)); - MOCK_METHOD(Orders, queryOpenedOrders, (const OrdersConstraints &), (override)); + MOCK_METHOD(OpenedOrderVector, queryOpenedOrders, (const OrdersConstraints &), (override)); MOCK_METHOD(int, cancelOpenedOrders, (const OrdersConstraints &), (override)); MOCK_METHOD(DepositsSet, queryRecentDeposits, (const DepositsConstraints &), (override)); diff --git a/src/api/exchanges/include/binanceprivateapi.hpp b/src/api/exchanges/include/binanceprivateapi.hpp index e6897e98..8bda0327 100644 --- a/src/api/exchanges/include/binanceprivateapi.hpp +++ b/src/api/exchanges/include/binanceprivateapi.hpp @@ -46,7 +46,7 @@ class BinancePrivate : public ExchangePrivate { bool canGenerateDepositAddress() const override { return true; } - Orders queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints = OrdersConstraints()) override; + OpenedOrderVector queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints = OrdersConstraints()) override; int cancelOpenedOrders(const OrdersConstraints& openedOrdersConstraints = OrdersConstraints()) override; diff --git a/src/api/exchanges/include/bithumbprivateapi.hpp b/src/api/exchanges/include/bithumbprivateapi.hpp index 5cb816cf..66929426 100644 --- a/src/api/exchanges/include/bithumbprivateapi.hpp +++ b/src/api/exchanges/include/bithumbprivateapi.hpp @@ -32,7 +32,7 @@ class BithumbPrivate : public ExchangePrivate { bool canGenerateDepositAddress() const override { return false; } - Orders queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints = OrdersConstraints()) override; + OpenedOrderVector queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints = OrdersConstraints()) override; int cancelOpenedOrders(const OrdersConstraints& openedOrdersConstraints = OrdersConstraints()) override; diff --git a/src/api/exchanges/include/huobiprivateapi.hpp b/src/api/exchanges/include/huobiprivateapi.hpp index 57b8d6ae..172ce73f 100644 --- a/src/api/exchanges/include/huobiprivateapi.hpp +++ b/src/api/exchanges/include/huobiprivateapi.hpp @@ -30,7 +30,7 @@ class HuobiPrivate : public ExchangePrivate { bool canGenerateDepositAddress() const override { return true; } - Orders queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints = OrdersConstraints()) override; + OpenedOrderVector queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints = OrdersConstraints()) override; int cancelOpenedOrders(const OrdersConstraints& openedOrdersConstraints = OrdersConstraints()) override; diff --git a/src/api/exchanges/include/krakenprivateapi.hpp b/src/api/exchanges/include/krakenprivateapi.hpp index 79c87b56..18f8b1d3 100644 --- a/src/api/exchanges/include/krakenprivateapi.hpp +++ b/src/api/exchanges/include/krakenprivateapi.hpp @@ -31,7 +31,7 @@ class KrakenPrivate : public ExchangePrivate { bool canGenerateDepositAddress() const override { return true; } - Orders queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints = OrdersConstraints()) override; + OpenedOrderVector queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints = OrdersConstraints()) override; int cancelOpenedOrders(const OrdersConstraints& openedOrdersConstraints = OrdersConstraints()) override; diff --git a/src/api/exchanges/include/kucoinprivateapi.hpp b/src/api/exchanges/include/kucoinprivateapi.hpp index c8b13127..31abbca4 100644 --- a/src/api/exchanges/include/kucoinprivateapi.hpp +++ b/src/api/exchanges/include/kucoinprivateapi.hpp @@ -30,7 +30,7 @@ class KucoinPrivate : public ExchangePrivate { bool canGenerateDepositAddress() const override { return true; } - Orders queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints = OrdersConstraints()) override; + OpenedOrderVector queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints = OrdersConstraints()) override; int cancelOpenedOrders(const OrdersConstraints& openedOrdersConstraints = OrdersConstraints()) override; diff --git a/src/api/exchanges/include/upbitprivateapi.hpp b/src/api/exchanges/include/upbitprivateapi.hpp index b0c967b3..4b59a16d 100644 --- a/src/api/exchanges/include/upbitprivateapi.hpp +++ b/src/api/exchanges/include/upbitprivateapi.hpp @@ -42,7 +42,7 @@ class UpbitPrivate : public ExchangePrivate { bool canGenerateDepositAddress() const override { return true; } - Orders queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints = OrdersConstraints()) override; + OpenedOrderVector queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints = OrdersConstraints()) override; int cancelOpenedOrders(const OrdersConstraints& openedOrdersConstraints = OrdersConstraints()) override; diff --git a/src/api/exchanges/src/binanceprivateapi.cpp b/src/api/exchanges/src/binanceprivateapi.cpp index dbc75c07..e2ed98bb 100644 --- a/src/api/exchanges/src/binanceprivateapi.cpp +++ b/src/api/exchanges/src/binanceprivateapi.cpp @@ -286,8 +286,8 @@ bool BinancePrivate::checkMarketAppendSymbol(Market mk, CurlPostData& params) { return true; } -Orders BinancePrivate::queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints) { - Orders openedOrders; +OpenedOrderVector BinancePrivate::queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints) { + OpenedOrderVector openedOrders; CurlPostData params; if (openedOrdersConstraints.isMarketDefined()) { // Symbol (which corresponds to a market) is optional - however, it costs 40 credits if omitted and should exist @@ -364,12 +364,12 @@ int BinancePrivate::cancelOpenedOrders(const OrdersConstraints& openedOrdersCons } } - Orders openedOrders = queryOpenedOrders(openedOrdersConstraints); + OpenedOrderVector openedOrders = queryOpenedOrders(openedOrdersConstraints); - using OrdersByMarketMap = std::unordered_map>; + using OrdersByMarketMap = std::unordered_map>; OrdersByMarketMap ordersByMarketMap; std::for_each(std::make_move_iterator(openedOrders.begin()), std::make_move_iterator(openedOrders.end()), - [&ordersByMarketMap](Order&& order) { + [&ordersByMarketMap](OpenedOrder&& order) { Market mk = order.market(); ordersByMarketMap[mk].push_back(std::move(order)); }); @@ -384,7 +384,7 @@ int BinancePrivate::cancelOpenedOrders(const OrdersConstraints& openedOrdersCons PrivateQuery(_curlHandle, _apiKey, HttpRequestType::kDelete, "/api/v3/openOrders", _queryDelay, params); nbOrdersCancelled += static_cast(cancelledOrders.size()); } else { - for (const Order& order : orders) { + for (const OpenedOrder& order : orders) { params.set("orderId", order.id()); PrivateQuery(_curlHandle, _apiKey, HttpRequestType::kDelete, "/api/v3/order", _queryDelay, params); } diff --git a/src/api/exchanges/src/bithumbprivateapi.cpp b/src/api/exchanges/src/bithumbprivateapi.cpp index af90b000..81367497 100644 --- a/src/api/exchanges/src/bithumbprivateapi.cpp +++ b/src/api/exchanges/src/bithumbprivateapi.cpp @@ -393,7 +393,7 @@ Wallet BithumbPrivate::DepositWalletFunc::operator()(CurrencyCode currencyCode) return wallet; } -Orders BithumbPrivate::queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints) { +OpenedOrderVector BithumbPrivate::queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints) { CurlPostData params; SmallVector orderCurrencies; @@ -428,7 +428,7 @@ Orders BithumbPrivate::queryOpenedOrders(const OrdersConstraints& openedOrdersCo } } - Orders openedOrders; + OpenedOrderVector openedOrders; if (openedOrdersConstraints.isPlacedTimeAfterDefined()) { params.append("after", TimestampToMs(openedOrdersConstraints.placedAfter())); } @@ -470,8 +470,8 @@ Orders BithumbPrivate::queryOpenedOrders(const OrdersConstraints& openedOrdersCo int BithumbPrivate::cancelOpenedOrders(const OrdersConstraints& openedOrdersConstraints) { // No faster way to cancel several orders at once with Bithumb, doing a simple for loop - Orders orders = queryOpenedOrders(openedOrdersConstraints); - for (const Order& order : orders) { + OpenedOrderVector orders = queryOpenedOrders(openedOrdersConstraints); + for (const OpenedOrder& order : orders) { TradeContext tradeContext(order.market(), order.side()); cancelOrderProcess(order.id(), tradeContext); } diff --git a/src/api/exchanges/src/huobiprivateapi.cpp b/src/api/exchanges/src/huobiprivateapi.cpp index c6c5efb6..03fcd2d8 100644 --- a/src/api/exchanges/src/huobiprivateapi.cpp +++ b/src/api/exchanges/src/huobiprivateapi.cpp @@ -188,7 +188,7 @@ Wallet HuobiPrivate::DepositWalletFunc::operator()(CurrencyCode currencyCode) { return wallet; } -Orders HuobiPrivate::queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints) { +OpenedOrderVector HuobiPrivate::queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints) { CurlPostData params; MarketSet markets; @@ -203,7 +203,7 @@ Orders HuobiPrivate::queryOpenedOrders(const OrdersConstraints& openedOrdersCons } json data = PrivateQuery(_curlHandle, _apiKey, HttpRequestType::kGet, "/v1/order/openOrders", std::move(params)); - Orders openedOrders; + OpenedOrderVector openedOrders; for (const json& orderDetails : data["data"]) { string marketStr = ToUpper(orderDetails["symbol"].get()); @@ -254,12 +254,12 @@ int HuobiPrivate::cancelOpenedOrders(const OrdersConstraints& openedOrdersConstr if (openedOrdersConstraints.isOrderIdOnlyDependent()) { return batchCancel(openedOrdersConstraints.orderIdSet()); } - Orders openedOrders = queryOpenedOrders(openedOrdersConstraints); + OpenedOrderVector openedOrders = queryOpenedOrders(openedOrdersConstraints); vector orderIds; orderIds.reserve(openedOrders.size()); std::transform(std::make_move_iterator(openedOrders.begin()), std::make_move_iterator(openedOrders.end()), - std::back_inserter(orderIds), [](Order&& order) -> OrderId&& { return std::move(order.id()); }); + std::back_inserter(orderIds), [](OpenedOrder&& order) -> OrderId&& { return std::move(order.id()); }); return batchCancel(OrdersConstraints::OrderIdSet(std::move(orderIds))); } diff --git a/src/api/exchanges/src/krakenprivateapi.cpp b/src/api/exchanges/src/krakenprivateapi.cpp index c042fc0c..574a346c 100644 --- a/src/api/exchanges/src/krakenprivateapi.cpp +++ b/src/api/exchanges/src/krakenprivateapi.cpp @@ -176,7 +176,7 @@ BalancePortfolio KrakenPrivate::queryAccountBalance(const BalanceOptions& balanc // Kraken returns total balance, including the amounts in use if (!withBalanceInUse) { // We need to query the opened orders to remove the balance in use - for (const Order& order : queryOpenedOrders()) { + for (const OpenedOrder& order : queryOpenedOrders()) { MonetaryAmount remVolume = order.remainingVolume(); switch (order.side()) { case TradeSide::kBuy: { @@ -282,10 +282,10 @@ Wallet KrakenPrivate::DepositWalletFunc::operator()(CurrencyCode currencyCode) { return wallet; } -Orders KrakenPrivate::queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints) { +OpenedOrderVector KrakenPrivate::queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints) { auto [res, err] = PrivateQuery(_curlHandle, _apiKey, "/private/OpenOrders", {{"trades", "true"}}); auto openedPartIt = res.find("open"); - Orders openedOrders; + OpenedOrderVector openedOrders; if (openedPartIt != res.end()) { MarketSet markets; @@ -339,8 +339,8 @@ int KrakenPrivate::cancelOpenedOrders(const OrdersConstraints& openedOrdersConst auto [cancelledOrders, err] = PrivateQuery(_curlHandle, _apiKey, "/private/CancelAll"); return cancelledOrders["count"].get(); } - Orders openedOrders = queryOpenedOrders(openedOrdersConstraints); - for (const Order& order : openedOrders) { + OpenedOrderVector openedOrders = queryOpenedOrders(openedOrdersConstraints); + for (const OpenedOrder& order : openedOrders) { cancelOrderProcess(order.id()); } return openedOrders.size(); diff --git a/src/api/exchanges/src/kucoinprivateapi.cpp b/src/api/exchanges/src/kucoinprivateapi.cpp index c924268c..2de92bdc 100644 --- a/src/api/exchanges/src/kucoinprivateapi.cpp +++ b/src/api/exchanges/src/kucoinprivateapi.cpp @@ -35,7 +35,7 @@ #include "kucoinpublicapi.hpp" #include "market.hpp" #include "monetaryamount.hpp" -#include "order.hpp" +#include "opened-order.hpp" #include "orderid.hpp" #include "ordersconstraints.hpp" #include "permanentcurloptions.hpp" @@ -221,7 +221,7 @@ Wallet KucoinPrivate::DepositWalletFunc::operator()(CurrencyCode currencyCode) { return wallet; } -Orders KucoinPrivate::queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints) { +OpenedOrderVector KucoinPrivate::queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints) { CurlPostData params{{"status", "active"}, {"tradeType", "TRADE"}}; if (openedOrdersConstraints.isCur1Defined()) { @@ -238,7 +238,7 @@ Orders KucoinPrivate::queryOpenedOrders(const OrdersConstraints& openedOrdersCon } json data = PrivateQuery(_curlHandle, _apiKey, HttpRequestType::kGet, "/api/v1/orders", std::move(params))["data"]; - Orders openedOrders; + OpenedOrderVector openedOrders; for (json& orderDetails : data["items"]) { std::string_view marketStr = orderDetails["symbol"].get(); std::size_t dashPos = marketStr.find('-'); @@ -293,8 +293,8 @@ int KucoinPrivate::cancelOpenedOrders(const OrdersConstraints& openedOrdersConst } return nbCancelledOrders; } - Orders openedOrders = queryOpenedOrders(openedOrdersConstraints); - for (const Order& order : openedOrders) { + OpenedOrderVector openedOrders = queryOpenedOrders(openedOrdersConstraints); + for (const OpenedOrder& order : openedOrders) { cancelOrderProcess(order.id()); } return openedOrders.size(); diff --git a/src/api/exchanges/src/upbitprivateapi.cpp b/src/api/exchanges/src/upbitprivateapi.cpp index 5c2724e5..dcd086b7 100644 --- a/src/api/exchanges/src/upbitprivateapi.cpp +++ b/src/api/exchanges/src/upbitprivateapi.cpp @@ -41,7 +41,7 @@ #include "httprequesttype.hpp" #include "market.hpp" #include "monetaryamount.hpp" -#include "order.hpp" +#include "opened-order.hpp" #include "orderid.hpp" #include "ordersconstraints.hpp" #include "permanentcurloptions.hpp" @@ -246,7 +246,7 @@ Wallet UpbitPrivate::DepositWalletFunc::operator()(CurrencyCode currencyCode) { return wallet; } -Orders UpbitPrivate::queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints) { +OpenedOrderVector UpbitPrivate::queryOpenedOrders(const OrdersConstraints& openedOrdersConstraints) { CurlPostData params{{"state", "wait"}}; if (openedOrdersConstraints.isCur1Defined()) { @@ -260,7 +260,7 @@ Orders UpbitPrivate::queryOpenedOrders(const OrdersConstraints& openedOrdersCons } json data = PrivateQuery(_curlHandle, _apiKey, HttpRequestType::kGet, "/v1/orders", std::move(params)); - Orders openedOrders; + OpenedOrderVector openedOrders; for (json& orderDetails : data) { std::string_view marketStr = orderDetails["market"].get(); std::size_t dashPos = marketStr.find('-'); @@ -300,8 +300,8 @@ Orders UpbitPrivate::queryOpenedOrders(const OrdersConstraints& openedOrdersCons int UpbitPrivate::cancelOpenedOrders(const OrdersConstraints& openedOrdersConstraints) { // No faster way to cancel several orders at once, doing a simple for loop - Orders openedOrders = queryOpenedOrders(openedOrdersConstraints); - for (const Order& order : openedOrders) { + OpenedOrderVector openedOrders = queryOpenedOrders(openedOrdersConstraints); + for (const OpenedOrder& order : openedOrders) { TradeContext tradeContext(order.market(), order.side()); cancelOrder(order.id(), tradeContext); } diff --git a/src/api/exchanges/test/exchangecommonapi_test.hpp b/src/api/exchanges/test/exchangecommonapi_test.hpp index c0fe6e91..afa222dc 100644 --- a/src/api/exchanges/test/exchangecommonapi_test.hpp +++ b/src/api/exchanges/test/exchangecommonapi_test.hpp @@ -220,9 +220,9 @@ class TestAPI { } if (exchangePrivateOpt && !sampleMarkets.empty()) { Market mk = sampleMarkets.front(); - Orders baseOpenedOrders = exchangePrivateOpt->queryOpenedOrders(OrdersConstraints(mk.base())); + OpenedOrderVector baseOpenedOrders = exchangePrivateOpt->queryOpenedOrders(OrdersConstraints(mk.base())); if (!baseOpenedOrders.empty()) { - const Order &openedOrder = baseOpenedOrders.front(); + const OpenedOrder &openedOrder = baseOpenedOrders.front(); EXPECT_TRUE(openedOrder.market().canTrade(mk.base())); EXPECT_LT(openedOrder.matchedVolume(), openedOrder.originalVolume()); } @@ -315,6 +315,6 @@ class TestAPI { TEST(TestAPIType##Test, DepositWallet) { testAPI.testDepositWallet(); } \ TEST(TestAPIType##Test, RecentDeposits) { testAPI.testRecentDeposits(); } \ TEST(TestAPIType##Test, RecentWithdraws) { testAPI.testRecentWithdraws(); } \ - TEST(TestAPIType##Test, Orders) { testAPI.testOpenedOrders(); } \ + TEST(TestAPIType##Test, OpenedOrders) { testAPI.testOpenedOrders(); } \ TEST(TestAPIType##Test, Trade) { testAPI.testTrade(); } } // namespace cct::api \ No newline at end of file diff --git a/src/api/interface/CMakeLists.txt b/src/api/interface/CMakeLists.txt index a17d1968..df998a70 100644 --- a/src/api/interface/CMakeLists.txt +++ b/src/api/interface/CMakeLists.txt @@ -3,6 +3,7 @@ aux_source_directory(src API_INTERFACE_SRC) add_library(coincenter_api-interface STATIC ${API_INTERFACE_SRC}) target_link_libraries(coincenter_api-interface PUBLIC coincenter_api-exchange) +target_link_libraries(coincenter_api-interface PUBLIC coincenter_serialization) target_link_libraries(coincenter_api-interface PRIVATE coincenter_monitoring) target_include_directories(coincenter_api-interface PUBLIC include) diff --git a/src/api/interface/include/exchange.hpp b/src/api/interface/include/exchange.hpp index ba5510dd..a4725c23 100644 --- a/src/api/interface/include/exchange.hpp +++ b/src/api/interface/include/exchange.hpp @@ -1,7 +1,9 @@ #pragma once +#include #include #include +#include #include "cct_exception.hpp" #include "currencycode.hpp" @@ -12,22 +14,30 @@ #include "exchangeprivateapi.hpp" #include "exchangepublicapi.hpp" #include "exchangepublicapitypes.hpp" +#include "market.hpp" #include "marketorderbook.hpp" #include "monetaryamount.hpp" #include "monetaryamountbycurrencyset.hpp" +#include "time-window.hpp" namespace cct { + +class AbstractMarketDataDeserializer; +class AbstractMarketDataSerializer; + class Exchange { public: using ExchangePublic = api::ExchangePublic; /// Builds a Exchange without private exchange. All private requests will be forbidden. - Exchange(const ExchangeConfig &exchangeConfig, api::ExchangePublic &exchangePublic); + Exchange(std::string_view dataDir, const ExchangeConfig &exchangeConfig, api::ExchangePublic &exchangePublic); /// Build a Exchange with both private and public exchanges - Exchange(const ExchangeConfig &exchangeConfig, api::ExchangePublic &exchangePublic, + Exchange(std::string_view dataDir, const ExchangeConfig &exchangeConfig, api::ExchangePublic &exchangePublic, api::ExchangePrivate &exchangePrivate); + ~Exchange(); + std::string_view name() const { return _exchangePublic.name(); } std::string_view keyName() const { return apiPrivate().keyName(); } @@ -86,16 +96,12 @@ class Exchange { return _exchangePublic.queryAllApproximatedOrderBooks(depth); } - MarketOrderBook queryOrderBook(Market mk, int depth = ExchangePublic::kDefaultDepth) { - return _exchangePublic.queryOrderBook(mk, depth); - } + MarketOrderBook queryOrderBook(Market mk, int depth = ExchangePublic::kDefaultDepth); MonetaryAmount queryLast24hVolume(Market mk) { return _exchangePublic.queryLast24hVolume(mk); } /// Retrieve an ordered vector of recent last trades - TradesVector queryLastTrades(Market mk, int nbTrades = ExchangePublic::kNbLastTradesDefault) { - return _exchangePublic.queryLastTrades(mk, nbTrades); - } + TradesVector queryLastTrades(Market mk, int nbTrades = ExchangePublic::kNbLastTradesDefault); /// Retrieve the last price of given market. MonetaryAmount queryLastPrice(Market mk) { return _exchangePublic.queryLastPrice(mk); } @@ -108,11 +114,24 @@ class Exchange { return name() == exchangeName.name() && (!exchangeName.isKeyNameDefined() || keyName() == exchangeName.keyName()); } + MarketSet pullAvailableMarketsForReplay(TimeWindow timeWindow); + + TradesVector pullTradesForReplay(Market market, TimeWindow timeWindow); + + MarketOrderBookVector pullMarketOrderBooksForReplay(Market market, TimeWindow timeWindow); + void updateCacheFile() const; + using trivially_relocatable = std::true_type; + private: + Exchange(std::string_view dataDir, const ExchangeConfig &exchangeConfig, api::ExchangePublic &exchangePublic, + api::ExchangePrivate *pExchangePrivate); + api::ExchangePublic &_exchangePublic; api::ExchangePrivate *_pExchangePrivate = nullptr; const ExchangeConfig &_exchangeConfig; + std::unique_ptr _marketDataDeserializerPtr; + std::unique_ptr _marketDataSerializerPtr; }; } // namespace cct diff --git a/src/api/interface/src/exchange.cpp b/src/api/interface/src/exchange.cpp index b6ed7dd8..2f8ffc5b 100644 --- a/src/api/interface/src/exchange.cpp +++ b/src/api/interface/src/exchange.cpp @@ -1,6 +1,7 @@ #include "exchange.hpp" #include +#include #include "cct_log.hpp" #include "currencycode.hpp" @@ -8,17 +9,46 @@ #include "exchangeconfig.hpp" #include "exchangeprivateapi.hpp" #include "exchangepublicapi.hpp" +#include "exchangepublicapitypes.hpp" + +#ifdef CCT_ENABLE_PROTO +#include "proto-market-data-deserializer.hpp" +#include "proto-market-data-serializer.hpp" +#else +#include "dummy-market-data-deserializer.hpp" +#include "dummy-market-data-serializer.hpp" +#endif namespace cct { -Exchange::Exchange(const ExchangeConfig &exchangeConfig, api::ExchangePublic &exchangePublic, +#ifdef CCT_ENABLE_PROTO +using MarketDataDeserializer = ProtoMarketDataDeserializer; +using MarketDataSerializer = ProtoMarketDataSerializer; +#else +using MarketDataDeserializer = DummyMarketDataDeserializer; +using MarketDataSerializer = DummyMarketDataSerializer; +#endif + +Exchange::Exchange(std::string_view dataDir, const ExchangeConfig &exchangeConfig, api::ExchangePublic &exchangePublic, api::ExchangePrivate &exchangePrivate) + : Exchange(dataDir, exchangeConfig, exchangePublic, std::addressof(exchangePrivate)) {} + +Exchange::Exchange(std::string_view dataDir, const ExchangeConfig &exchangeConfig, api::ExchangePublic &exchangePublic) + : Exchange(dataDir, exchangeConfig, exchangePublic, nullptr) {} + +Exchange::Exchange(std::string_view dataDir, const ExchangeConfig &exchangeConfig, api::ExchangePublic &exchangePublic, + api::ExchangePrivate *pExchangePrivate) : _exchangePublic(exchangePublic), - _pExchangePrivate(std::addressof(exchangePrivate)), - _exchangeConfig(exchangeConfig) {} + _pExchangePrivate(pExchangePrivate), + _exchangeConfig(exchangeConfig), + _marketDataDeserializerPtr(_exchangeConfig.withMarketDataSerialization() + ? new MarketDataDeserializer(dataDir, exchangePublic.name()) + : nullptr), + _marketDataSerializerPtr(_exchangeConfig.withMarketDataSerialization() + ? new MarketDataSerializer(dataDir, exchangePublic.name()) + : nullptr) {} -Exchange::Exchange(const ExchangeConfig &exchangeConfig, api::ExchangePublic &exchangePublic) - : _exchangePublic(exchangePublic), _exchangeConfig(exchangeConfig) {} +Exchange::~Exchange() = default; // declared here to have definition of ~MarketDataSerializer bool Exchange::canWithdraw(CurrencyCode currencyCode, const CurrencyExchangeFlatSet ¤cyExchangeSet) const { if (_exchangeConfig.excludedCurrenciesWithdrawal().contains(currencyCode)) { @@ -41,6 +71,47 @@ bool Exchange::canDeposit(CurrencyCode currencyCode, const CurrencyExchangeFlatS return lb->canDeposit(); } +MarketOrderBook Exchange::queryOrderBook(Market mk, int depth) { + auto marketOrderBook = _exchangePublic.queryOrderBook(mk, depth); + if (_marketDataSerializerPtr) { + _marketDataSerializerPtr->push(marketOrderBook); + } + return marketOrderBook; +} + +/// Retrieve an ordered vector of recent last trades +TradesVector Exchange::queryLastTrades(Market mk, int nbTrades) { + auto lastTrades = _exchangePublic.queryLastTrades(mk, nbTrades); + if (_marketDataSerializerPtr) { + _marketDataSerializerPtr->push(lastTrades); + } + return lastTrades; +} + +MarketSet Exchange::pullAvailableMarketsForReplay(TimeWindow timeWindow) { + MarketSet ret; + if (_marketDataDeserializerPtr) { + ret = MarketSet(_marketDataDeserializerPtr->pullMarketOrderBooksMarkets(timeWindow)); + const auto tradesMarkets = _marketDataDeserializerPtr->pullTradeMarkets(timeWindow); + ret.insert(tradesMarkets.begin(), tradesMarkets.end()); + } + return ret; +} + +TradesVector Exchange::pullTradesForReplay(Market market, TimeWindow timeWindow) { + if (_marketDataDeserializerPtr) { + return _marketDataDeserializerPtr->pullTrades(market, timeWindow); + } + return {}; +} + +MarketOrderBookVector Exchange::pullMarketOrderBooksForReplay(Market market, TimeWindow timeWindow) { + if (_marketDataDeserializerPtr) { + return _marketDataDeserializerPtr->pullMarketOrderBooks(market, timeWindow); + } + return {}; +} + void Exchange::updateCacheFile() const { _exchangePublic.updateCacheFile(); if (_pExchangePrivate != nullptr) { diff --git a/src/api/interface/src/exchangepool.cpp b/src/api/interface/src/exchangepool.cpp index 1d04d5ef..6886f76f 100644 --- a/src/api/interface/src/exchangepool.cpp +++ b/src/api/interface/src/exchangepool.cpp @@ -31,6 +31,7 @@ ExchangePool::ExchangePool(const CoincenterInfo& coincenterInfo, FiatConverter& _krakenPublic(_coincenterInfo, _fiatConverter, _commonAPI), _kucoinPublic(_coincenterInfo, _fiatConverter, _commonAPI), _upbitPublic(_coincenterInfo, _fiatConverter, _commonAPI) { + const auto dataDir = coincenterInfo.dataDir(); for (std::string_view exchangeStr : kSupportedExchanges) { api::ExchangePublic* exchangePublic; if (exchangeStr == "binance") { @@ -81,10 +82,10 @@ ExchangePool::ExchangePool(const CoincenterInfo& coincenterInfo, FiatConverter& } } - _exchanges.emplace_back(exchangeConfig, *exchangePublic, *exchangePrivate); + _exchanges.emplace_back(dataDir, exchangeConfig, *exchangePublic, *exchangePrivate); } } else { - _exchanges.emplace_back(exchangeConfig, *exchangePublic); + _exchanges.emplace_back(dataDir, exchangeConfig, *exchangePublic); } } _exchanges.shrink_to_fit(); diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt index 3e5f241e..26a6a692 100644 --- a/src/engine/CMakeLists.txt +++ b/src/engine/CMakeLists.txt @@ -6,6 +6,7 @@ target_link_libraries(coincenter_engine PUBLIC coincenter_api-common) target_link_libraries(coincenter_engine PUBLIC coincenter_api-exchange) target_link_libraries(coincenter_engine PUBLIC coincenter_api-interface) target_link_libraries(coincenter_engine PUBLIC coincenter_objects) +target_link_libraries(coincenter_engine PUBLIC coincenter_trading-common) target_include_directories(coincenter_engine PUBLIC include) add_unit_test( diff --git a/src/engine/include/coincentercommand.hpp b/src/engine/include/coincentercommand.hpp index fe736333..366dc7b6 100644 --- a/src/engine/include/coincentercommand.hpp +++ b/src/engine/include/coincentercommand.hpp @@ -12,6 +12,7 @@ #include "market.hpp" #include "monetaryamount.hpp" #include "ordersconstraints.hpp" +#include "timedef.hpp" #include "tradeoptions.hpp" #include "withdrawoptions.hpp" #include "withdrawsconstraints.hpp" @@ -50,15 +51,14 @@ class CoincenterCommand { CoincenterCommand& setCur1(CurrencyCode cur1); CoincenterCommand& setCur2(CurrencyCode cur2); + CoincenterCommand& setDuration(Duration dur); + CoincenterCommand& setPercentageAmount(bool value = true); CoincenterCommand& withBalanceInUse(bool value = true); bool isPublic() const; bool isPrivate() const { return !isPublic(); } - bool isReadOnly() const; - bool isWrite() const { return !isReadOnly(); } - const ExchangeNames& exchangeNames() const { return _exchangeNames; } const OrdersConstraints& ordersConstraints() const { return std::get(_specialOptions); } @@ -86,13 +86,16 @@ class CoincenterCommand { bool isPercentageAmount() const { return _isPercentageAmount; } bool withBalanceInUse() const { return _withBalanceInUse; } + Duration duration() const { return std::get(_specialOptions); } + bool operator==(const CoincenterCommand&) const noexcept = default; using trivially_relocatable = std::integral_constant && is_trivially_relocatable_v>::type; private: - using SpecialOptions = std::variant; + using SpecialOptions = + std::variant; ExchangeNames _exchangeNames; SpecialOptions _specialOptions; diff --git a/src/engine/include/coincenteroptions.hpp b/src/engine/include/coincenteroptions.hpp index 7a11cf91..86a6b252 100644 --- a/src/engine/include/coincenteroptions.hpp +++ b/src/engine/include/coincenteroptions.hpp @@ -95,6 +95,9 @@ class CoincenterCmdLineOptions { std::string_view lastTrades; + std::optional replay; + std::optional replayMarkets; + CommandLineOptionalInt repeats; int nbLastTrades = api::ExchangePublic::kNbLastTradesDefault; int monitoringPort = CoincenterCmdLineOptionsDefinitions::kDefaultMonitoringPort; diff --git a/src/engine/include/coincenteroptionsdef.hpp b/src/engine/include/coincenteroptionsdef.hpp index 7aeaafcc..fd603482 100644 --- a/src/engine/include/coincenteroptionsdef.hpp +++ b/src/engine/include/coincenteroptionsdef.hpp @@ -309,14 +309,14 @@ struct CoincenterAllowedOptions : private CoincenterCmdLineOptionsDefinitions { &OptValueType::minAge}, {{{"Private queries", 3902}, "--max-age", "