Skip to content

Commit 600ac42

Browse files
committed
Implement order book checksum in kraken
1 parent db68d00 commit 600ac42

File tree

3 files changed

+80
-31
lines changed

3 files changed

+80
-31
lines changed

include/ccapi_cpp/ccapi_util_private.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -958,7 +958,7 @@ class Decimal {
958958
}
959959
}
960960

961-
explicit Decimal(std::string_view originalValue) {
961+
explicit Decimal(std::string_view originalValue, bool checksumEnabled=false) {
962962
if (originalValue.empty()) {
963963
throw std::invalid_argument("Decimal constructor input value cannot be empty");
964964
}
@@ -972,7 +972,7 @@ class Decimal {
972972
}
973973
std::string fixedPointValue = std::string(originalValue.substr(this->sign ? 0 : 1, this->sign ? foundE : foundE - 1));
974974
auto foundDot = fixedPointValue.find('.');
975-
if (foundDot != std::string::npos) {
975+
if (foundDot != std::string::npos and not checksumEnabled) {
976976
fixedPointValue.erase(fixedPointValue.find_last_not_of('0') + 1);
977977
fixedPointValue.erase(fixedPointValue.find_last_not_of('.') + 1);
978978
}
@@ -1017,9 +1017,9 @@ class Decimal {
10171017
}
10181018
if (foundDot != std::string::npos) {
10191019
this->frac = fixedPointValue.substr(foundDot + 1);
1020-
// if (!keepTrailingZero) {
1021-
this->frac.erase(this->frac.find_last_not_of('0') + 1);
1022-
// }
1020+
if (!checksumEnabled) {
1021+
this->frac.erase(this->frac.find_last_not_of('0') + 1);
1022+
}
10231023
}
10241024
} else {
10251025
auto found = originalValue.find('.');
@@ -1033,9 +1033,9 @@ class Decimal {
10331033
}
10341034
if (found != std::string::npos) {
10351035
this->frac = originalValue.substr(found + 1);
1036-
// if (!keepTrailingZero) {
1037-
this->frac.erase(this->frac.find_last_not_of('0') + 1);
1038-
// }
1036+
if (not checksumEnabled) {
1037+
this->frac.erase(this->frac.find_last_not_of('0') + 1);
1038+
}
10391039
}
10401040
}
10411041

include/ccapi_cpp/service/ccapi_market_data_service.h

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -804,15 +804,15 @@ class MarketDataService : public Service {
804804
for (auto& y : detail) {
805805
const auto& price = y.at(MarketDataMessage::DataFieldType::PRICE);
806806
const auto& size = y.at(MarketDataMessage::DataFieldType::SIZE);
807-
Decimal decimalPrice(price);
807+
Decimal decimalPrice(price, this->sessionOptions.enableCheckOrderBookChecksum);
808808
snapshotBid.emplace(decimalPrice, std::string(size));
809809
}
810810
CCAPI_LOGGER_TRACE("lastNToString(snapshotBid, " + toString(maxMarketDepth) + ") = " + lastNToString(snapshotBid, maxMarketDepth));
811811
} else if (type == MarketDataMessage::DataType::ASK) {
812812
for (auto& y : detail) {
813813
const auto& price = y.at(MarketDataMessage::DataFieldType::PRICE);
814814
const auto& size = y.at(MarketDataMessage::DataFieldType::SIZE);
815-
Decimal decimalPrice(price);
815+
Decimal decimalPrice(price, this->sessionOptions.enableCheckOrderBookChecksum);
816816
snapshotAsk.emplace(decimalPrice, std::string(size));
817817
}
818818
CCAPI_LOGGER_TRACE("firstNToString(snapshotAsk, " + toString(maxMarketDepth) + ") = " + firstNToString(snapshotAsk, maxMarketDepth));
@@ -894,14 +894,14 @@ class MarketDataService : public Service {
894894
for (auto& y : detail) {
895895
const auto& price = y.at(MarketDataMessage::DataFieldType::PRICE);
896896
const auto& size = y.at(MarketDataMessage::DataFieldType::SIZE);
897-
Decimal decimalPrice(price);
897+
Decimal decimalPrice(price, this->sessionOptions.enableCheckOrderBookChecksum);
898898
this->updateOrderBook(snapshotBid, decimalPrice, size, this->sessionOptions.enableCheckOrderBookChecksum);
899899
}
900900
} else if (type == MarketDataMessage::DataType::ASK) {
901901
for (auto& y : detail) {
902902
const auto& price = y.at(MarketDataMessage::DataFieldType::PRICE);
903903
const auto& size = y.at(MarketDataMessage::DataFieldType::SIZE);
904-
Decimal decimalPrice(price);
904+
Decimal decimalPrice(price, this->sessionOptions.enableCheckOrderBookChecksum);
905905
this->updateOrderBook(snapshotAsk, decimalPrice, size, this->sessionOptions.enableCheckOrderBookChecksum);
906906
}
907907
} else {
@@ -1098,7 +1098,7 @@ class MarketDataService : public Service {
10981098
this->highByConnectionIdChannelIdSymbolIdMap[wsConnectionPtr->id][channelId][symbolId] = Decimal(price);
10991099
this->lowByConnectionIdChannelIdSymbolIdMap[wsConnectionPtr->id][channelId][symbolId] = Decimal(price);
11001100
} else {
1101-
Decimal decimalPrice(price);
1101+
Decimal decimalPrice(price, this->sessionOptions.enableCheckOrderBookChecksum);
11021102
if (decimalPrice > this->highByConnectionIdChannelIdSymbolIdMap[wsConnectionPtr->id][channelId][symbolId]) {
11031103
this->highByConnectionIdChannelIdSymbolIdMap[wsConnectionPtr->id][channelId][symbolId] = decimalPrice;
11041104
}
@@ -1372,15 +1372,15 @@ class MarketDataService : public Service {
13721372
for (auto& y : detail) {
13731373
const auto& price = y.at(MarketDataMessage::DataFieldType::PRICE);
13741374
const auto& size = y.at(MarketDataMessage::DataFieldType::SIZE);
1375-
Decimal decimalPrice(price);
1375+
Decimal decimalPrice(price, this->sessionOptions.enableCheckOrderBookChecksum);
13761376
snapshotBid.emplace(decimalPrice, std::string(size));
13771377
}
13781378
CCAPI_LOGGER_TRACE("lastNToString(snapshotBid, " + toString(maxMarketDepth) + ") = " + lastNToString(snapshotBid, maxMarketDepth));
13791379
} else if (type == MarketDataMessage::DataType::ASK) {
13801380
for (auto& y : detail) {
13811381
const auto& price = y.at(MarketDataMessage::DataFieldType::PRICE);
13821382
const auto& size = y.at(MarketDataMessage::DataFieldType::SIZE);
1383-
Decimal decimalPrice(price);
1383+
Decimal decimalPrice(price, this->sessionOptions.enableCheckOrderBookChecksum);
13841384
snapshotAsk.emplace(decimalPrice, std::string(size));
13851385
}
13861386
CCAPI_LOGGER_TRACE("firstNToString(snapshotAsk, " + toString(maxMarketDepth) + ") = " + firstNToString(snapshotAsk, maxMarketDepth));
@@ -1520,14 +1520,14 @@ class MarketDataService : public Service {
15201520
for (const auto& y : detail) {
15211521
const auto& price = y.at(MarketDataMessage::DataFieldType::PRICE);
15221522
const auto& size = y.at(MarketDataMessage::DataFieldType::SIZE);
1523-
Decimal decimalPrice(price);
1523+
Decimal decimalPrice(price, that->sessionOptions.enableCheckOrderBookChecksum);
15241524
snapshotBid.emplace(decimalPrice, std::string(size));
15251525
}
15261526
} else if (type == MarketDataMessage::DataType::ASK) {
15271527
for (const auto& y : detail) {
15281528
const auto& price = y.at(MarketDataMessage::DataFieldType::PRICE);
15291529
const auto& size = y.at(MarketDataMessage::DataFieldType::SIZE);
1530-
Decimal decimalPrice(price);
1530+
Decimal decimalPrice(price, that->sessionOptions.enableCheckOrderBookChecksum);
15311531
snapshotAsk.emplace(decimalPrice, std::string(size));
15321532
}
15331533
}
@@ -1548,14 +1548,14 @@ class MarketDataService : public Service {
15481548
for (const auto& y : detail) {
15491549
const auto& price = y.at(MarketDataMessage::DataFieldType::PRICE);
15501550
const auto& size = y.at(MarketDataMessage::DataFieldType::SIZE);
1551-
Decimal decimalPrice(price);
1551+
Decimal decimalPrice(price, that->sessionOptions.enableCheckOrderBookChecksum);
15521552
that->updateOrderBook(snapshotBid, decimalPrice, size, that->sessionOptions.enableCheckOrderBookChecksum);
15531553
}
15541554
} else if (type == MarketDataMessage::DataType::ASK) {
15551555
for (const auto& y : detail) {
15561556
const auto& price = y.at(MarketDataMessage::DataFieldType::PRICE);
15571557
const auto& size = y.at(MarketDataMessage::DataFieldType::SIZE);
1558-
Decimal decimalPrice(price);
1558+
Decimal decimalPrice(price, that->sessionOptions.enableCheckOrderBookChecksum);
15591559
that->updateOrderBook(snapshotAsk, decimalPrice, size, that->sessionOptions.enableCheckOrderBookChecksum);
15601560
}
15611561
}
@@ -1671,6 +1671,7 @@ class MarketDataService : public Service {
16711671
Event& event, std::vector<MarketDataMessage>& marketDataMessageList) {}
16721672

16731673
virtual std::string calculateOrderBookChecksum(const std::map<Decimal, std::string>& snapshotBid, const std::map<Decimal, std::string>& snapshotAsk) {
1674+
CCAPI_LOGGER_DEBUG("calculateOrderBookChecksum is not implemented for this exchange");
16741675
return {};
16751676
}
16761677

include/ccapi_cpp/service/ccapi_market_data_service_kraken.h

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,47 @@ class MarketDataServiceKraken : public MarketDataService {
5050
}
5151
}
5252

53+
static void processForChecksum(std::string& str) {
54+
str.erase(std::remove(str.begin(), str.end(), '.'), str.end());
55+
UtilString::ltrimInPlace(str, "0");
56+
}
57+
58+
std::vector<std::string> extractTop(const std::map<Decimal, std::string>& snapshot, bool reverse = false) {
59+
std::vector<std::string> result;
60+
int count = 0;
61+
62+
auto process_item = [&](auto it) {
63+
std::string price = toString(it->first);
64+
processForChecksum(price);
65+
result.push_back(std::move(price));
66+
67+
std::string volume = it->second;
68+
processForChecksum(volume);
69+
result.push_back(std::move(volume));
70+
};
71+
72+
if (reverse) {
73+
for (auto it = snapshot.rbegin(); it != snapshot.rend() && count < 10; ++it, ++count) {
74+
process_item(it);
75+
}
76+
} else {
77+
for (auto it = snapshot.begin(); it != snapshot.end() && count < 10; ++it, ++count) {
78+
process_item(it);
79+
}
80+
}
81+
return result;
82+
};
83+
84+
std::string calculateOrderBookChecksum(const std::map<Decimal, std::string>& snapshotBid, const std::map<Decimal, std::string>& snapshotAsk) override {
85+
auto csAskData = extractTop(snapshotAsk);
86+
auto csBidData = extractTop(snapshotBid, true);
87+
88+
std::string csStr = UtilString::join(csAskData, "") + UtilString::join(csBidData, "");
89+
uint_fast32_t csCalc = UtilAlgorithm::crc(csStr.begin(), csStr.end());
90+
CCAPI_LOGGER_DEBUG("csStr: " + csStr + ", csCalc: " + intToHex(csCalc));
91+
return intToHex(csCalc);
92+
}
93+
5394
std::vector<std::string> createSendStringList(std::shared_ptr<WsConnection> wsConnectionPtr) override {
5495
std::vector<std::string> sendStringList;
5596
for (const auto& subscriptionListByChannelIdSymbolId : this->subscriptionListByConnectionIdChannelIdSymbolIdMap.at(wsConnectionPtr->id)) {
@@ -186,19 +227,26 @@ class MarketDataServiceKraken : public MarketDataService {
186227
marketDataMessage.exchangeSubscriptionId = exchangeSubscriptionId;
187228
marketDataMessage.tp = latestTp;
188229
marketDataMessage.recapType = MarketDataMessage::RecapType::NONE;
230+
if (this->sessionOptions.enableCheckOrderBookChecksum) {
231+
if (anonymous2.HasMember("c")) {
232+
CCAPI_LOGGER_DEBUG("Checksum for " + symbolId + ": " + anonymous2["c"].GetString());
233+
this->orderBookChecksumByConnectionIdSymbolIdMap[wsConnectionPtr->id][symbolId] =
234+
intToHex(static_cast<uint_fast32_t>(static_cast<uint32_t>(std::stoul(anonymous2["c"].GetString()))));
235+
}
236+
}
189237
if (anonymous2.HasMember("b")) {
190238
for (const auto& x : anonymous2["b"].GetArray()) {
191239
MarketDataMessage::TypeForDataPoint dataPoint;
192-
dataPoint.emplace(MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalStringView(x[0].GetString()));
193-
dataPoint.emplace(MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalStringView(x[1].GetString()));
240+
dataPoint.emplace(MarketDataMessage::DataFieldType::PRICE, x[0].GetString());
241+
dataPoint.emplace(MarketDataMessage::DataFieldType::SIZE, x[1].GetString());
194242
marketDataMessage.data[MarketDataMessage::DataType::BID].emplace_back(std::move(dataPoint));
195243
}
196244
}
197245
if (anonymous2.HasMember("a")) {
198246
for (const auto& x : anonymous2["a"].GetArray()) {
199247
MarketDataMessage::TypeForDataPoint dataPoint;
200-
dataPoint.emplace(MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalStringView(x[0].GetString()));
201-
dataPoint.emplace(MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalStringView(x[1].GetString()));
248+
dataPoint.emplace(MarketDataMessage::DataFieldType::PRICE, x[0].GetString());
249+
dataPoint.emplace(MarketDataMessage::DataFieldType::SIZE, x[1].GetString());
202250
marketDataMessage.data[MarketDataMessage::DataType::ASK].emplace_back(std::move(dataPoint));
203251
}
204252
}
@@ -211,14 +259,14 @@ class MarketDataServiceKraken : public MarketDataService {
211259
marketDataMessage.tp = timeReceived;
212260
for (const auto& x : anonymous["bs"].GetArray()) {
213261
MarketDataMessage::TypeForDataPoint dataPoint;
214-
dataPoint.emplace(MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalStringView(x[0].GetString()));
215-
dataPoint.emplace(MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalStringView(x[1].GetString()));
262+
dataPoint.emplace(MarketDataMessage::DataFieldType::PRICE, x[0].GetString());
263+
dataPoint.emplace(MarketDataMessage::DataFieldType::SIZE, x[1].GetString());
216264
marketDataMessage.data[MarketDataMessage::DataType::BID].emplace_back(std::move(dataPoint));
217265
}
218266
for (const auto& x : anonymous["as"].GetArray()) {
219267
MarketDataMessage::TypeForDataPoint dataPoint;
220-
dataPoint.emplace(MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalStringView(x[0].GetString()));
221-
dataPoint.emplace(MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalStringView(x[1].GetString()));
268+
dataPoint.emplace(MarketDataMessage::DataFieldType::PRICE, x[0].GetString());
269+
dataPoint.emplace(MarketDataMessage::DataFieldType::SIZE, x[1].GetString());
222270
marketDataMessage.data[MarketDataMessage::DataType::ASK].emplace_back(std::move(dataPoint));
223271
}
224272
marketDataMessageList.emplace_back(std::move(marketDataMessage));
@@ -235,8 +283,8 @@ class MarketDataServiceKraken : public MarketDataService {
235283
tp += std::chrono::nanoseconds(timePair.second);
236284
marketDataMessage.tp = tp;
237285
MarketDataMessage::TypeForDataPoint dataPoint;
238-
dataPoint.emplace(MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalStringView(x[0].GetString()));
239-
dataPoint.emplace(MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalStringView(x[1].GetString()));
286+
dataPoint.emplace(MarketDataMessage::DataFieldType::PRICE, x[0].GetString());
287+
dataPoint.emplace(MarketDataMessage::DataFieldType::SIZE, x[1].GetString());
240288
dataPoint.emplace(MarketDataMessage::DataFieldType::IS_BUYER_MAKER, std::string_view(x[3].GetString()) == "s" ? "1" : "0");
241289
marketDataMessage.data[MarketDataMessage::DataType::TRADE].emplace_back(std::move(dataPoint));
242290
marketDataMessageList.emplace_back(std::move(marketDataMessage));
@@ -372,8 +420,8 @@ class MarketDataServiceKraken : public MarketDataService {
372420
tp += std::chrono::nanoseconds(timePair.second);
373421
marketDataMessage.tp = tp;
374422
MarketDataMessage::TypeForDataPoint dataPoint;
375-
dataPoint.emplace(MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalStringView(x[0].GetString()));
376-
dataPoint.emplace(MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalStringView(x[1].GetString()));
423+
dataPoint.emplace(MarketDataMessage::DataFieldType::PRICE, x[0].GetString());
424+
dataPoint.emplace(MarketDataMessage::DataFieldType::SIZE, x[1].GetString());
377425
dataPoint.emplace(MarketDataMessage::DataFieldType::IS_BUYER_MAKER, std::string_view(x[3].GetString()) == "s" ? "1" : "0");
378426
marketDataMessage.data[MarketDataMessage::DataType::TRADE].emplace_back(std::move(dataPoint));
379427
marketDataMessageList.emplace_back(std::move(marketDataMessage));

0 commit comments

Comments
 (0)