Skip to content

Commit

Permalink
[EN-7164] Add candle events: CandleSymbol
Browse files Browse the repository at this point in the history
  • Loading branch information
AnatolyKalin committed Aug 14, 2023
1 parent 74f7595 commit 0c1e65c
Show file tree
Hide file tree
Showing 4 changed files with 296 additions and 34 deletions.
258 changes: 252 additions & 6 deletions include/dxfeed_graal_cpp_api/event/candle/CandleSymbol.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
#include "CandleSession.hpp"
#include "CandleSymbolAttribute.hpp"

#include <variant>
#include <unordered_map>
#include <string>
#include <cstdint>
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <variant>

namespace dxfcpp {

Expand All @@ -29,15 +29,222 @@ using CandleSymbolAttributeVariant =

struct SymbolWrapper;

/**
* Symbol that should be used with {@link DXFeedSubscription} class
* to subscribe for {@link Candle} events. {@code DXFeedSubscription} also accepts a string
* representation of the candle symbol for subscription.
*
* <h3>String representation</h3>
*
* The string representation of the candle symbol consist of a {@link #getBaseSymbol() baseSymbol} followed by
* an optional '&amp;' with an {@link #getExchange() exchange} code letter and followed by
* an optional list of comma-separated key=value pairs in curly braces:
*
* <p>{@code <baseSymbol> [ '&' <exchange> ] [ '{' <key1>=<value1> [ ',' <key2>=<value2> ] ... '}' ]}
*
* <p>Properties of the candle symbol correspond to the keys in the string representation in the following way:
*
* <ul>
* <li>Empty key corresponds to {@link #getPeriod() period} &mdash; aggregation period of this symbol.
* The period value is composed of an optional
* {@link CandlePeriod#getValue() value} which defaults to 1 when not specified, followed by
* a {@link CandlePeriod#getType() type} string which is defined by one of the
* {@link CandleType} enum values and can be abbreviated to first letters. For example, a daily candle of "IBM" base
* symbol can be specified as "IBM{=d}" and 15 minute candle on it as "IBM{=15m}". The shortest
* possible abbreviation for {@link CandleType#MONTH CandleType.MONTH} is "mo", so the monthly
* candle can be specified as "IBM{=mo}". When period is not specified, then the
* {@link CandlePeriod#TICK TICK} aggregation period is assumed as default. Note, that tick aggregation may
* not be available on the demo system which is limited to a subset of symbols and aggregation periods.
* <li>"price" key corresponds to {@link #getPrice() price} &mdash; price type attribute of this symbol.
* The {@link CandlePrice} enum defines possible values with {@link CandlePrice#LAST LAST} being default.
* For legacy backwards-compatibility purposes, most of the price values cannot be abbreviated, so a one-minute candle
* of "EUR/USD" bid price shall be specified with "EUR/USD{=m,price=bid}" candle symbol string. However,
* the {@link CandlePrice#SETTLEMENT SETTLEMENT} can be abbreviated to "s", so a daily candle on
* "/ES" futures settlement prices can be specified with "/ES{=d,price=s}" string.
* <li>"tho" key with a value of "true" corresponds to {@link #getSession() session} set to {@link CandleSession#REGULAR}
* which limits the candle to trading hours only, so a 133 tick candles on "GOOG" base symbol collected over
* trading hours only can be specified with "GOOG{=133t,tho=true}" string. Note, that the default daily candles for
* US equities are special for historical reasons and correspond to the way US equity exchange report their
* daily summary data. The volume the US equity default daily candle corresponds to the total daily traded volume,
* while open, high, low, and close correspond to the regular trading hours only.
* <li>"a" key corresponds to {@link #getAlignment() alignment} &mdash; alignment attribute of this symbol.
* The {@link CandleAlignment} enum defines possible values with {@link CandleAlignment#MIDNIGHT MIDNIGHT} being default.
* The alignment values can be abbreviated to the first letter. So, a 1 hour candle on a symbol "AAPL" that starts
* at the regular trading session at 9:30 am ET can be specified with "AAPL{=h,a=s,tho=true}". Contrast that
* to the "AAPL{=h,tho=true}" candle that is aligned at midnight and thus starts at 9:00 am.
* <li>"pl" key corresponds to {@link #getPriceLevel() price level} &mdash; price level attribute of this symbol.
* The {@link CandlePriceLevel} defines additional axis to split candles within particular price corridor in
* addition to {@link CandlePeriod} attribute with the default value {@code Double.NaN}. So a one-minute candles
* of "AAPL" with price level 0.1 shall be specified with "AAPL{=m,pl=0.1}".
* </ul>
*
* Keys in the candle symbol are case-sensitive, while values are not. The {@link #valueOf(String)} method parses
* any valid string representation into a candle symbol object.
* The result of the candle symbol
* {@link #toString()} method is always normalized: keys are ordered lexicographically, values are in lower-case
* and are abbreviated to their shortest possible form.
*/
struct DXFCPP_EXPORT CandleSymbol {

private:

std::string symbol_{};
std::string baseSymbol_{};
//std::unordered_map<CandleSymbolAttributeType, CandleSymbolAttributeT> attributes_{};
std::optional<CandleExchange> exchange_{};
std::optional<CandlePrice> price_{};
std::optional<CandleSession> session_{};
std::optional<CandlePeriod> period_{};
std::optional<CandleAlignment> alignment_{};
std::optional<CandlePriceLevel> priceLevel_{};

static std::string changeAttribute(std::string symbol, const CandleSymbolAttributeVariant &attribute) noexcept {
return std::visit(
[symbol = std::move(symbol)](auto &&a) {
return a.changeAttributeForSymbol(symbol);
},
attribute);
}

static std::string changeAttributes(std::string symbol,
std::initializer_list<CandleSymbolAttributeVariant> attributes) noexcept {
for (const auto &a : attributes) {
symbol = changeAttribute(symbol, a);
}

return symbol;
}

static std::string normalize(std::string symbol) noexcept {
symbol = CandlePrice::normalizeAttributeForSymbol(symbol);
symbol = CandleSession::normalizeAttributeForSymbol(symbol);
symbol = CandlePeriod::normalizeAttributeForSymbol(symbol);
symbol = CandleAlignment::normalizeAttributeForSymbol(symbol);
symbol = CandlePriceLevel::normalizeAttributeForSymbol(symbol);

return symbol;
}

void initTransientFields() noexcept {
baseSymbol_ = MarketEventSymbols::getBaseSymbol(symbol_);

if (!exchange_) {
exchange_.emplace(CandleExchange::getAttributeForSymbol(symbol_));
}

if (!price_) {
price_ = CandlePrice::getAttributeForSymbol(symbol_);
}

if (!session_) {
session_ = CandleSession::getAttributeForSymbol(symbol_);
}

if (!period_) {
period_ = CandlePeriod::getAttributeForSymbol(symbol_);
}

if (!alignment_) {
alignment_ = CandleAlignment::getAttributeForSymbol(symbol_);
}

if (!priceLevel_) {
priceLevel_ = CandlePriceLevel::getAttributeForSymbol(symbol_);
}
}

explicit CandleSymbol(std::string symbol) noexcept : symbol_{normalize(std::move(symbol))} {
initTransientFields();
}

CandleSymbol(std::string symbol, const CandleSymbolAttributeVariant &attribute) noexcept
: symbol_{normalize(changeAttribute(std::move(symbol), attribute))} {
// TODO: check attributes
initTransientFields();
}

CandleSymbol(std::string symbol, std::initializer_list<CandleSymbolAttributeVariant> attributes) noexcept
: symbol_{normalize(changeAttributes(std::move(symbol), attributes))} {
// TODO: check attributes
initTransientFields();
}

public:
/**
* Returns base market symbol without attributes.
*
* @return base market symbol without attributes.
*/
const std::string &getBaseSymbol() const & noexcept {
return baseSymbol_;
}

/**
* Returns exchange attribute of this symbol.
*
* @return exchange attribute of this symbol.
*/
const std::optional<CandleExchange> &getExchange() const & noexcept {
return exchange_;
}

/**
* Returns price type attribute of this symbol.
*
* @return price type attribute of this symbol.
*/
const std::optional<CandlePrice> &getPrice() const & noexcept {
return price_;
}

/**
* Returns session attribute of this symbol.
*
* @return session attribute of this symbol.
*/
const std::optional<CandleSession> &getSession() const & noexcept {
return session_;
}

/**
* Returns aggregation period of this symbol.
*
* @return aggregation period of this symbol.
*/
const std::optional<CandlePeriod> &getPeriod() const & noexcept {
return period_;
}

/**
* Returns alignment attribute of this symbol.
*
* @return alignment attribute of this symbol.
*/
const std::optional<CandleAlignment> &getAlignment() const & noexcept {
return alignment_;
}

/**
* Returns price level attribute of this symbol.
*
* @return price level attribute of this symbol.
*/
const std::optional<CandlePriceLevel> &getPriceLevel() const & noexcept {
return priceLevel_;
}

/**
* Returns string representation of this symbol.
* The string representation can be transformed back into symbol object
* using {@link #valueOf(String) valueOf(String)} method.
*
* @return string representation of this symbol.
*/
const std::string &toString() const & noexcept {
return symbol_;
}

bool operator==(const CandleSymbol &candleSymbol) const noexcept {
return symbol_ == candleSymbol.symbol_;
}

CandleSymbol() noexcept = default;

Expand All @@ -48,6 +255,45 @@ struct DXFCPP_EXPORT CandleSymbol {
static void freeGraal(void *graal) noexcept;

static CandleSymbol fromGraal(void *graal) noexcept;

/**
* Converts the given string symbol into the candle symbol object.
*
* @param symbol the string symbol.
* @return the candle symbol object.
*/
static CandleSymbol valueOf(std::string symbol) noexcept {
return CandleSymbol{std::move(symbol)};
}

/**
* Converts the given string symbol into the candle symbol object with the specified attribute set.
*
* @param symbol the string symbol.
* @param attribute the attribute to set.
* @return the candle symbol object.
*/
static CandleSymbol valueOf(std::string symbol, const CandleSymbolAttributeVariant &attribute) noexcept {
return {std::move(symbol), attribute};
}

/**
* Converts the given string symbol into the candle symbol object with the specified attributes set.
*
* @param symbol the string symbol.
* @param attributes more attributes to set.
* @return the candle symbol object.
*/
static CandleSymbol valueOf(std::string symbol,
std::initializer_list<CandleSymbolAttributeVariant> attributes) noexcept {
return {std::move(symbol), attributes};
}
};

} // namespace dxfcpp
} // namespace dxfcpp

template <> struct std::hash<dxfcpp::CandleSymbol> {
std::size_t operator()(const dxfcpp::CandleSymbol &candleSymbol) const noexcept {
return std::hash<std::string>{}(candleSymbol.toString());
}
};
29 changes: 27 additions & 2 deletions include/dxfeed_graal_cpp_api/event/market/MarketEventSymbols.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,31 @@ struct DXFCPP_EXPORT MarketEventSymbols {
return i == symbol.length() ? result : result + symbol.substr(i);
}

/**
* Returns base symbol without exchange code and attributes.
*
* @param symbol symbol.
* @return base symbol without exchange code and attributes.
*/
static DXFCPP_CXX20_CONSTEXPR_STRING std::string getBaseSymbol(const std::string &symbol) noexcept {
return getBaseSymbolInternal(symbol, getLengthWithoutAttributesInternal(symbol));
}

/**
* Changes base symbol while leaving exchange code and attributes intact.
* @param symbol old symbol.
* @param baseSymbol new base symbol.
* @return new symbol with new base symbol and old symbol's exchange code and attributes.
*/
static DXFCPP_CXX20_CONSTEXPR_STRING std::string changeBaseSymbol(const std::string &symbol,
const std::string &baseSymbol) noexcept {
auto i = getLengthWithoutAttributesInternal(symbol);

return hasExchangeCodeInternal(symbol, i) ? baseSymbol + EXCHANGE_SEPARATOR + symbol[i - 1] + symbol.substr(i)
: i == symbol.length() ? baseSymbol
: baseSymbol + symbol.substr(i);
}

/**
* Returns value of the attribute with the specified key.
* The result is std::nullopt if attribute with the specified key is not found.
Expand Down Expand Up @@ -118,11 +143,11 @@ struct DXFCPP_EXPORT MarketEventSymbols {
static constexpr char ATTRIBUTES_SEPARATOR = ',';
static constexpr char ATTRIBUTE_VALUE = '=';

static bool hasExchangeCodeInternal(const std::string &symbol, std::size_t length) {
static constexpr bool hasExchangeCodeInternal(const std::string &symbol, std::size_t length) noexcept {
return length >= 2 && symbol[length - 2] == EXCHANGE_SEPARATOR;
}

static std::string getBaseSymbolInternal(const std::string &symbol, std::size_t length) {
static DXFCPP_CXX20_CONSTEXPR_STRING std::string getBaseSymbolInternal(const std::string &symbol, std::size_t length) noexcept {
return hasExchangeCodeInternal(symbol, length) ? symbol.substr(0, length - 2) : symbol.substr(0, length);
}

Expand Down
27 changes: 9 additions & 18 deletions src/event/candle/CandleSymbol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,9 @@ void *CandleSymbol::toGraal() const noexcept {
Debugger::debug("CandleSymbol::toGraal()");
}

// auto *graalSymbol = new (std::nothrow)
// dxfg_indexed_event_subscription_symbol_t{{INDEXED_EVENT_SUBSCRIPTION},
// dxfcpp::bit_cast<dxfg_symbol_t *>(eventSymbol_->toGraal()),
// dxfcpp::bit_cast<dxfg_indexed_event_source_t *>(source_.toGraal())};
//
// return dxfcpp::bit_cast<void *>(graalSymbol);

return nullptr;
auto *graalSymbol = new (std::nothrow) dxfg_candle_symbol_t{{CANDLE}, createCString(symbol_)};

return dxfcpp::bit_cast<void *>(graalSymbol);
}

void CandleSymbol::freeGraal(void *graal) noexcept {
Expand All @@ -32,12 +27,10 @@ void CandleSymbol::freeGraal(void *graal) noexcept {
return;
}

// auto *graalSymbol = dxfcpp::bit_cast<dxfg_indexed_event_subscription_symbol_t *>(graal);
//
// SymbolWrapper::freeGraal(graalSymbol->symbol);
// IndexedEventSource::freeGraal(graalSymbol->source);
//
// delete graalSymbol;
auto *graalSymbol = dxfcpp::bit_cast<dxfg_candle_symbol_t *>(graal);

delete[] graalSymbol->symbol;
delete graalSymbol;
}

CandleSymbol CandleSymbol::fromGraal(void *graal) noexcept {
Expand All @@ -49,11 +42,9 @@ CandleSymbol CandleSymbol::fromGraal(void *graal) noexcept {
return {};
}

// auto *graalSymbol = dxfcpp::bit_cast<dxfg_indexed_event_subscription_symbol_t *>(graal);
//
// return {SymbolWrapper::fromGraal(graalSymbol->symbol), IndexedEventSource::fromGraal(graalSymbol->source)};
auto *graalSymbol = dxfcpp::bit_cast<dxfg_candle_symbol_t *>(graal);

return {};
return CandleSymbol{graalSymbol->symbol};
}

}
Loading

0 comments on commit 0c1e65c

Please sign in to comment.