Skip to content

Commit

Permalink
[MDAPI-216][C++][Console] Add PriceLevelBook sample (#66)
Browse files Browse the repository at this point in the history
  • Loading branch information
AnatolyKalin authored Jan 9, 2025
2 parents d3aeae3 + d122184 commit 36a8979
Show file tree
Hide file tree
Showing 14 changed files with 847 additions and 28 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,7 @@ if (DXFCXX_BUILD_SAMPLES)
add_subdirectory(samples/cpp/ReconnectSample)
add_subdirectory(samples/cpp/IncOrderSnapshotSample)
add_subdirectory(samples/cpp/MultipleMarketDepthSample)
add_subdirectory(samples/cpp/PriceLevelBookSample)
endif ()

add_subdirectory(docs)
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,9 @@ be downloaded from [Release](https://github.com/dxFeed/dxfeed-graal-cxx-api/rele
demonstrates how to subscribe to the Order event and handle snapshots and updates
- [x] [MultipleMarketDepthSample](samples/cpp/MultipleMarketDepthSample/src/main.cpp)
demonstrates how to use the `MarketDepthModel` to manage and display order books for multiple symbols
- [x] [PriceLevelBookSample](samples/cpp/PriceLevelBookSample/src/main.cpp)
demonstrates how to compile a price level book (market by price) that aggregates individual orders
(market by order)

### Schedule

Expand All @@ -394,11 +397,11 @@ be downloaded from [Release](https://github.com/dxFeed/dxfeed-graal-cxx-api/rele

### UI

- [ ] [MarketDepthModelSample](samples/cpp/MarketDepthModelSample/src/main.cpp)
- [ ] [MarketDepthModelSample](samples/cpp/UI/MarketDepthModelSample/src/main.cpp)
demonstrates how to draw a Market Depth.
- [ ] [CandleChartSample](samples/cpp/CandleChartSample/src/main.cpp)
- [ ] [CandleChartSample](samples/cpp/UI/CandleChartSample/src/main.cpp)
demonstrates how to draw a Candlestick Chart.
- [ ] [PriceLevelBookSample](samples/cpp/PriceLevelBookSample/src/main.cpp)
- [ ] [PriceLevelBookSample](samples/cpp/UI/PriceLevelBookSample/src/main.cpp)
demonstrates how to compile a price level book (market by price) that aggregates individual orders
(market by order)

Expand Down
4 changes: 3 additions & 1 deletion ReleaseNotes.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
* **\[MDAPI-216]\[C++][Console]** Added PriceLevelBook sample

## v4.0.0

* **\[MDAPI-38]\[C++]** Add MultipleMarketDepthSample sample
* **\[MDAPI-38]\[C++]** Added MultipleMarketDepthSample sample
* Added `MultipleMarketDepthSample` sample.
* Added `AuthSample` sample.
* Added `ReconnectSample` sample.
Expand Down
4 changes: 2 additions & 2 deletions include/dxfeed_graal_cpp_api/internal/Common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ static constexpr std::int64_t floorMod(std::int64_t x, std::int64_t y) {

static const double NaN = std::numeric_limits<double>::quiet_NaN();

static inline bool equals(double a, double b, double eps = std::numeric_limits<double>::epsilon()) {
static bool equals(double a, double b, double eps = std::numeric_limits<double>::epsilon()) {
if (std::isnan(a) || std::isnan(b)) {
return false;
}
Expand All @@ -242,7 +242,7 @@ static inline bool equals(double a, double b, double eps = std::numeric_limits<d
}

template <typename T, typename U>
static inline bool equals(T a, U b, double eps = std::numeric_limits<double>::epsilon()) {
static bool equals(T a, U b, double eps = std::numeric_limits<double>::epsilon()) {
if (std::isnan(static_cast<double>(a)) || std::isnan(static_cast<double>(b))) {
return false;
}
Expand Down
7 changes: 3 additions & 4 deletions include/dxfeed_graal_cpp_api/model/IndexedTxModel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,13 +374,12 @@ struct DXFCPP_EXPORT IndexedTxModel final : IndexedTxModelImpl, RequireMakeShare
std::shared_ptr<TxModelListenerCommon> listener_;

public:
IndexedTxModel(typename IndexedTxModel::LockExternalConstructionTag,
JavaObjectHandle<IndexedTxModelTag> &&handle)
IndexedTxModel(typename IndexedTxModel::LockExternalConstructionTag, JavaObjectHandle<IndexedTxModelTag> &&handle)
: handle_(std::move(handle)) {
}

IndexedTxModel(typename IndexedTxModel::LockExternalConstructionTag,
JavaObjectHandle<IndexedTxModelTag> &&handle, std::shared_ptr<TxModelListenerCommon> listener)
IndexedTxModel(typename IndexedTxModel::LockExternalConstructionTag, JavaObjectHandle<IndexedTxModelTag> &&handle,
std::shared_ptr<TxModelListenerCommon> listener)
: handle_(std::move(handle)), listener_(std::move(listener)) {
}

Expand Down
25 changes: 12 additions & 13 deletions include/dxfeed_graal_cpp_api/model/MarketDepthModel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ struct SymbolWrapper;
* std::cout << std::format("{0:^15}|{1:^15} || {0:^15}|{1:^15}\n", "Price", "Size");
* std::cout << std::format("{:-^66}\n", "");
*
* for (auto buyIt = buy.begin(), sellIt = sell.begin(); buyIt != buy.end() && sellIt != sell.end();) {
* for (auto buyIt = buy.begin(), sellIt = sell.begin(); buyIt != buy.end() || sellIt != sell.end();) {
* std::string row{};
* if (buyIt != buy.end()) {
* row += std::format("{:>14.4f} | {:<14.2f}", (*buyIt)->getPrice(), (*buyIt)->getSize());
Expand Down Expand Up @@ -130,8 +130,7 @@ template <Derived<OrderBase> O> struct DXFCPP_EXPORT MarketDepthModel final : Re
builder_ = IndexedTxModel<O>::newBuilder();
}

~Builder() override {
}
~Builder() override = default;

/**
* Sets the DXFeed for the model being created.
Expand Down Expand Up @@ -179,9 +178,10 @@ template <Derived<OrderBase> O> struct DXFCPP_EXPORT MarketDepthModel final : Re
* @param onEventsReceived The callback.
* @return The builder instance.
*/
std::shared_ptr<Builder> withListener(std::function<void(const std::vector<std::shared_ptr<O>> & /* buy */,
const std::vector<std::shared_ptr<O>> & /* sell */)>
onEventsReceived) {
std::shared_ptr<Builder>
withListener(std::function<void(const std::vector<std::shared_ptr<O>> & /* buy */,
const std::vector<std::shared_ptr<O>> & /* sell */)>
onEventsReceived) {
this->listener_ = MarketDepthModelListener<O>::create(onEventsReceived);

return this->template sharedAs<Builder>();
Expand Down Expand Up @@ -431,7 +431,7 @@ template <Derived<OrderBase> O> struct DXFCPP_EXPORT MarketDepthModel final : Re
isChanged_ = false;
snapshot_.clear();

auto limit = isDepthLimitUnbounded() ? std::numeric_limits<std::size_t>::max() : depthLimit_.load();
const auto limit = isDepthLimitUnbounded() ? std::numeric_limits<std::size_t>::max() : depthLimit_.load();
std::size_t i = 0;

for (auto it = orders_.begin(); i < limit && it != orders_.end(); ++it) {
Expand Down Expand Up @@ -514,9 +514,8 @@ template <Derived<OrderBase> O> struct DXFCPP_EXPORT MarketDepthModel final : Re
* Clears orders from the set by source.
*
* @param source The source to clear orders by.
* @return `true` if any order was removed.
*/
bool clearBySource(const IndexedEventSource &source) {
void clearBySource(const IndexedEventSource &source) {
std::lock_guard lock(mutex_);

std::size_t size = orders_.size();
Expand All @@ -527,7 +526,7 @@ template <Derived<OrderBase> O> struct DXFCPP_EXPORT MarketDepthModel final : Re
}
}

return orders_.size() != size;
isChanged_ = orders_.size() != size;
}

std::vector<std::shared_ptr<O>> toVector() {
Expand Down Expand Up @@ -722,7 +721,7 @@ template <Derived<OrderBase> O> struct DXFCPP_EXPORT MarketDepthModel final : Re
}

/**
* @return The depth limit of the book.
* @return The depth limit of the model.
*/
std::size_t getDepthLimit() const {
std::lock_guard guard(mtx_);
Expand All @@ -731,7 +730,7 @@ template <Derived<OrderBase> O> struct DXFCPP_EXPORT MarketDepthModel final : Re
}

/**
* Sets the depth limit of the book.
* Sets the depth limit of the model.
*
* @param depthLimit The new depth limit value.
*/
Expand All @@ -750,7 +749,7 @@ template <Derived<OrderBase> O> struct DXFCPP_EXPORT MarketDepthModel final : Re
}

/**
* @return The aggregation period of the book.
* @return The aggregation period of the model.
*/
std::int64_t getAggregationPeriod() const {
std::lock_guard guard(mtx_);
Expand Down
2 changes: 1 addition & 1 deletion include/dxfeed_graal_cpp_api/model/TxModelListener.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ DXFCXX_DISABLE_MSC_WARNINGS_PUSH(4251)

#include "../entity/SharedEntity.hpp"
#include "../event/EventType.hpp"
#include "../event/TimeSeriesEvent.hpp"
#include "../event/IndexedEvent.hpp"
#include "../event/IndexedEventSource.hpp"
#include "../event/TimeSeriesEvent.hpp"
#include "../internal/Handler.hpp"
#include "../internal/Id.hpp"
#include "../internal/JavaObjectHandle.hpp"
Expand Down
1 change: 0 additions & 1 deletion samples/cpp/AuthSample/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

#include <dxfeed_graal_cpp_api/api.hpp>

#include <chrono>
#include <mutex>
#include <random>
#include <string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ struct MultipleMarketDepthModel : dxfcpp::RequireMakeShared<MultipleMarketDepthM
/// A builder class for creating instances of MultipleMarketDepthModel.
struct Builder : dxfcpp::RequireMakeShared<Builder> {
friend struct MultipleMarketDepthModel;

private:
std::shared_ptr<typename dxfcpp::MarketDepthModel<O>::Builder> builder_{};

Expand Down
6 changes: 3 additions & 3 deletions samples/cpp/MultipleMarketDepthSample/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ using namespace dxfcpp::literals;
using namespace std::literals;

// This sample program demonstrates how to use the MultipleMarketDepthModel
int main() {
int main(int /*argc*/, char * /*argv*/[]) {
try {
const auto address = "demo.dxfeed.com:7300";

Expand All @@ -40,12 +40,12 @@ int main() {
const auto maxCount = std::max(book->buy.size(), book->sell.size());

for (std::size_t i = 0; i < maxCount; i++) {
auto buyTable = i < book->buy.size() ? fmt::format("Buy [Source: {}, Size: {:5}, Price: {:8}]",
auto buyTable = i < book->buy.size() ? fmt::format("Buy [Source: {}, Size: {:8.2f}, Price: {:8.4f}]",
book->buy[i]->getSource().toString(),
book->buy[i]->getSize(), book->buy[i]->getPrice())
: "Buy [None]"s;
auto sellTable = i < book->sell.size()
? fmt::format("Sell [Source: {}, Size: {:5}, Price: {:8}]",
? fmt::format("Sell [Source: {}, Size: {:8.2f}, Price: {:8.4f}]",
book->sell[i]->getSource().toString(), book->sell[i]->getSize(),
book->sell[i]->getPrice())
: "Sell [None]"s;
Expand Down
56 changes: 56 additions & 0 deletions samples/cpp/PriceLevelBookSample/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Copyright (c) 2024 Devexperts LLC.
# SPDX-License-Identifier: MPL-2.0

cmake_minimum_required(VERSION 3.21)

if (POLICY CMP0092)
cmake_policy(SET CMP0092 NEW)
endif ()

if (POLICY CMP0135)
cmake_policy(SET CMP0135 NEW)
endif ()

project(PriceLevelBookSample LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_C_STANDARD 11)
set(CXX_EXTENSIONS OFF)
set(C_EXTENSIONS OFF)

if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(CMAKE_MACOSX_RPATH ON)
set(CMAKE_SKIP_BUILD_RPATH ON)
set(CMAKE_BUILD_WITH_INSTALL_RPATH ON)
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH OFF)
set(CMAKE_BUILD_RPATH_USE_ORIGIN ON)
set(CMAKE_INSTALL_RPATH "@loader_path/../${CMAKE_INSTALL_LIBDIR};@loader_path;@executable_path;@executable_path/../Frameworks")
elseif (UNIX)
set(CMAKE_SKIP_BUILD_RPATH ON)
set(CMAKE_BUILD_WITH_INSTALL_RPATH ON)
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH OFF)
set(CMAKE_BUILD_RPATH_USE_ORIGIN ON)
set(CMAKE_INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:$ORIGIN/../lib64:$ORIGIN/../lib:$ORIGIN")
endif ()

add_executable(${PROJECT_NAME} src/main.cpp)

target_include_directories(${PROJECT_NAME} PRIVATE ../../../third_party/range-v3-0.12/include)

target_link_libraries(${PROJECT_NAME} PRIVATE dxfcxx::static fmt::fmt-header-only)

if (DXFCXX_FEATURE_STACKTRACE)
LinkStacktrace(${PROJECT_NAME})
endif ()

add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:dxfcxx::graal>
$<TARGET_FILE_DIR:${PROJECT_NAME}>)

if (DXFCXX_INSTALL AND DXFCXX_INSTALL_SAMPLES)
install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})

if (WIN32)
install(FILES $<TARGET_FILE:dxfcxx::graal> DESTINATION ${CMAKE_INSTALL_BINDIR})
endif ()
endif ()
105 changes: 105 additions & 0 deletions samples/cpp/PriceLevelBookSample/src/PriceLevel.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright (c) 2024 Devexperts LLC.
// SPDX-License-Identifier: MPL-2.0

#pragma once

#include <dxfeed_graal_cpp_api/api.hpp>

#include <string>

/// Represents a price level in a price level book.
struct PriceLevel {
private:
std::optional<std::string> eventSymbol_;
const dxfcpp::IndexedEventSource &eventSource_;
const dxfcpp::Side &side_;
double price_;
double size_;

public:
/**
* Initializes a new default instance.
*/
PriceLevel() : eventSource_{dxfcpp::IndexedEventSource::DEFAULT}, side_{dxfcpp::Side::UNDEFINED}, price_{}, size_{} {
}

/**
* Initializes a new instance by copying an existing price level.
*
* @param pl The price level to copy.
*/
PriceLevel(const PriceLevel &pl)
: eventSymbol_{pl.eventSymbol_}, eventSource_{pl.eventSource_}, side_{pl.side_}, price_{pl.price_},
size_(pl.size_) {
}

/**
* Initializes a new instance using an order.
*
* @param order The order to initialize the price level from.
*/
explicit PriceLevel(const std::shared_ptr<dxfcpp::OrderBase> &order)
: eventSymbol_{order->getEventSymbolOpt()}, eventSource_{order->getSource()}, side_{order->getOrderSide()},
price_{order->getPrice()}, size_{order->getSize()} {
}

/// Returns the optional event symbol.
const std::optional<std::string> &getEventSymbol() const {
return eventSymbol_;
}

/// Returns the event source.
const dxfcpp::IndexedEventSource &getEventSource() const {
return eventSource_;
}

/// Returns the side of the price level.
const dxfcpp::Side &getSide() const {
return side_;
}

/// Returns the price of the price level.
double getPrice() const {
return price_;
}

/// Returns the size of the price level.
double getSize() const {
return size_;
}

/**
* Sets the size of the price level.
*
* @param size The new price level size.
*/
void setSize(const double size) {
size_ = size;
}

/**
* Compares two price levels without taking price into account.
*
* @param pl1 The first price level to compare.
* @param pl2 The second price level to compare.
* @return Returns `true` if the event symbols are not nullptr, are equal, and the sides are equal.
*/
friend bool operator==(const std::shared_ptr<PriceLevel> &pl1, const std::shared_ptr<PriceLevel> &pl2) {
if (pl1->getEventSymbol() == std::nullopt || pl2->getEventSymbol() == std::nullopt) {
return false;
}

if (pl1->getEventSymbol() != pl2->getEventSymbol()) {
return false;
}

return pl1->getSide() == pl2->getSide();
}

/// Returns a string representation of this price level.
std::string toString() const {
return "PriceLevel{" + eventSymbol_.value_or(dxfcpp::String::NUL) + ", source=" + eventSource_.toString() +
", side=" + side_.toString() + ", price=" + dxfcpp::toString(price_) +
", size=" + dxfcpp::toString(size_) + "}";
}
};
Loading

0 comments on commit 36a8979

Please sign in to comment.