From 7f909129f1a9f91de7e161f207a76bd355bcdb05 Mon Sep 17 00:00:00 2001 From: obergaba <78508013+obergaba@users.noreply.github.com> Date: Tue, 5 Nov 2024 21:23:33 +0100 Subject: [PATCH] feat: Updated Bitget execution services for futures markets from V1 to V2; add: balance and positions updates (#485) * feat: Updated Bitget execution services for futures markets from V1 to V2 * feat: Updated Bitget execution services for futures markets from V1 to V2; add: balance and positions updates --- include/ccapi_cpp/ccapi_macro.h | 3 + ...cution_management_service_bitget_futures.h | 155 +++++++++++++----- 2 files changed, 113 insertions(+), 45 deletions(-) diff --git a/include/ccapi_cpp/ccapi_macro.h b/include/ccapi_cpp/ccapi_macro.h index bfa5aad1..a09e85b8 100644 --- a/include/ccapi_cpp/ccapi_macro.h +++ b/include/ccapi_cpp/ccapi_macro.h @@ -543,6 +543,9 @@ #ifndef CCAPI_INSTRUMENT #define CCAPI_INSTRUMENT "INSTRUMENT" #endif +#ifndef CCAPI_MARGIN_MODE +#define CCAPI_MARGIN_MODE "MARGIN_MODE" +#endif #ifndef CCAPI_EM_ORDER_STATUS #define CCAPI_EM_ORDER_STATUS "STATUS" #endif diff --git a/include/ccapi_cpp/service/ccapi_execution_management_service_bitget_futures.h b/include/ccapi_cpp/service/ccapi_execution_management_service_bitget_futures.h index b6eda5e0..c923bf6d 100644 --- a/include/ccapi_cpp/service/ccapi_execution_management_service_bitget_futures.h +++ b/include/ccapi_cpp/service/ccapi_execution_management_service_bitget_futures.h @@ -10,7 +10,7 @@ class ExecutionManagementServiceBitgetFutures : public ExecutionManagementServic ServiceContextPtr serviceContextPtr) : ExecutionManagementServiceBitgetBase(eventHandler, sessionOptions, sessionConfigs, serviceContextPtr) { this->exchangeName = CCAPI_EXCHANGE_NAME_BITGET_FUTURES; - this->baseUrlWs = sessionConfigs.getUrlWebsocketBase().at(this->exchangeName) + "/mix/v1/stream"; + this->baseUrlWs = sessionConfigs.getUrlWebsocketBase().at(this->exchangeName) + "/v2/ws/private"; this->baseUrlRest = sessionConfigs.getUrlRestBase().at(this->exchangeName); this->setHostRestFromUrlRest(this->baseUrlRest); this->setHostWsFromUrlWs(this->baseUrlWs); @@ -31,15 +31,15 @@ class ExecutionManagementServiceBitgetFutures : public ExecutionManagementServic this->apiSecretName = CCAPI_BITGET_FUTURES_API_SECRET; this->apiPassphraseName = CCAPI_BITGET_FUTURES_API_PASSPHRASE; this->setupCredential({this->apiKeyName, this->apiSecretName, this->apiPassphraseName}); - this->createOrderTarget = "/api/mix/v1/order/placeOrder"; - this->cancelOrderTarget = "/api/mix/v1/order/cancel-order"; - this->getOrderTarget = "/api/mix/v1/order/detail"; - this->getOpenOrdersTarget = "/api/mix/v1/order/current"; - this->getAllOpenOrdersTarget = "/api/mix/v1/order/marginCoinCurrent"; - this->cancelOpenOrdersTarget = "/api/mix/v1/order/cancel-all-orders"; - this->getAccountBalancesTarget = "/api/mix/v1/account/accounts"; - this->getAccountPositionsTarget = "/api/mix/v1/position/singlePosition"; - this->getAccountAllPositionsTarget = "/api/mix/v1/position/allPosition"; + this->createOrderTarget = "/api/v2/mix/order/place-order"; + this->cancelOrderTarget = "/api/v2/mix/order/cancel-order"; + this->getOrderTarget = "/api/v2/mix/order/detail"; + this->getOpenOrdersTarget = "/api/v2/mix/order/orders-pending"; + this->getAllOpenOrdersTarget = "/api/v2/mix/order/orders-pending"; + this->cancelOpenOrdersTarget = "/api/v2/mix/order/cancel-all-orders"; + this->getAccountBalancesTarget = "/api/v2/mix/account/accounts"; + this->getAccountPositionsTarget = "/api/v2/mix/position/single-position"; + this->getAccountAllPositionsTarget = "/api/v2/mix/position/all-position"; } virtual ~ExecutionManagementServiceBitgetFutures() {} #ifndef CCAPI_EXPOSE_INTERNAL @@ -49,6 +49,7 @@ class ExecutionManagementServiceBitgetFutures : public ExecutionManagementServic void appendParam(Request::Operation operation, rj::Value& rjValue, rj::Document::AllocatorType& allocator, const std::map& param, const std::map standardizationMap = { {CCAPI_MARGIN_ASSET, "marginCoin"}, + {CCAPI_MARGIN_MODE, "marginMode"}, {CCAPI_EM_ORDER_SIDE, "side"}, {CCAPI_EM_ORDER_QUANTITY, "size"}, {CCAPI_EM_ORDER_LIMIT_PRICE, "price"}, @@ -65,8 +66,9 @@ class ExecutionManagementServiceBitgetFutures : public ExecutionManagementServic } void appendParam(std::string& queryString, const std::map& param, const std::map standardizationMap = { + {CCAPI_MARGIN_ASSET, "marginCoin"}, {CCAPI_EM_ORDER_ID, "orderId"}, - {CCAPI_EM_CLIENT_ORDER_ID, "clientOrderId"}, + {CCAPI_EM_CLIENT_ORDER_ID, "clientOid"}, {CCAPI_SYMBOL_ID, "symbol"}, {CCAPI_INSTRUMENT_TYPE, "productType"}, }) { @@ -97,9 +99,6 @@ class ExecutionManagementServiceBitgetFutures : public ExecutionManagementServic if (param.find("orderType") == param.end()) { document.AddMember("orderType", rj::Value("limit").Move(), allocator); } - if (param.find("timeInForceValue") == param.end()) { - document.AddMember("timeInForceValue", rj::Value("normal").Move(), allocator); - } if (!symbolId.empty()) { this->appendSymbolId(document, allocator, symbolId); } @@ -219,20 +218,34 @@ class ExecutionManagementServiceBitgetFutures : public ExecutionManagementServic {CCAPI_EM_ORDER_SIDE, std::make_pair("side", JsonDataType::STRING)}, {CCAPI_EM_ORDER_QUANTITY, std::make_pair("size", JsonDataType::STRING)}, {CCAPI_EM_ORDER_LIMIT_PRICE, std::make_pair("price", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_CUMULATIVE_FILLED_QUANTITY, std::make_pair("filledQty", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_STATUS, std::make_pair("state", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_CUMULATIVE_FILLED_QUANTITY, std::make_pair("baseVolume", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_STATUS, std::make_pair("status", JsonDataType::STRING)}, {CCAPI_EM_ORDER_INSTRUMENT, std::make_pair("symbol", JsonDataType::STRING)}}; - const rj::Value& data = document["data"]; if (operation == Request::Operation::CREATE_ORDER || operation == Request::Operation::CANCEL_ORDER || operation == Request::Operation::GET_ORDER) { + const rj::Value& data = document["data"]; Element element; this->extractOrderInfo(element, data, extractionFieldNameMap); elementList.emplace_back(std::move(element)); } else if (operation == Request::Operation::GET_OPEN_ORDERS) { + const rj::Value& data = document["data"]["entrustedList"]; for (const auto& x : data.GetArray()) { Element element; this->extractOrderInfo(element, x, extractionFieldNameMap); elementList.emplace_back(std::move(element)); } + } else if (operation == Request::Operation::CANCEL_OPEN_ORDERS) { + const rj::Value& successList = document["data"]["successList"]; + for (const auto& x : successList.GetArray()) { + Element element; + this->extractOrderInfo(element, x, extractionFieldNameMap); + elementList.emplace_back(std::move(element)); + } + const rj::Value& failureList = document["data"]["failureList"]; + for (const auto& x : failureList.GetArray()) { + Element element; + this->extractOrderInfo(element, x, extractionFieldNameMap); + elementList.emplace_back(std::move(element)); + } } } void extractAccountInfoFromRequest(std::vector& elementList, const Request& request, const Request::Operation operation, @@ -242,7 +255,7 @@ class ExecutionManagementServiceBitgetFutures : public ExecutionManagementServic for (const auto& x : document["data"].GetArray()) { Element element; element.insert(CCAPI_EM_ASSET, x["marginCoin"].GetString()); - std::string available = x["crossMaxAvailable"].GetString(); + std::string available = x["crossedMaxAvailable"].GetString(); element.insert(CCAPI_EM_QUANTITY_AVAILABLE_FOR_TRADING, available); std::string locked = x["locked"].GetString(); element.insert(CCAPI_EM_QUANTITY_TOTAL, (Decimal(available).add(Decimal(locked))).toString()); @@ -256,8 +269,9 @@ class ExecutionManagementServiceBitgetFutures : public ExecutionManagementServic element.insert(CCAPI_MARGIN_ASSET, x["marginCoin"].GetString()); element.insert(CCAPI_EM_POSITION_SIDE, x["holdSide"].GetString()); element.insert(CCAPI_EM_POSITION_QUANTITY, x["total"].GetString()); - element.insert(CCAPI_EM_POSITION_ENTRY_PRICE, x["averageOpenPrice"].GetString()); + element.insert(CCAPI_EM_POSITION_ENTRY_PRICE, x["openPriceAvg"].GetString()); element.insert(CCAPI_EM_POSITION_LEVERAGE, x["leverage"].GetString()); + element.insert(CCAPI_EM_UNREALIZED_PNL, x["unrealizedPL"].GetString()); elementList.emplace_back(std::move(element)); } } break; @@ -269,8 +283,8 @@ class ExecutionManagementServiceBitgetFutures : public ExecutionManagementServic const std::map> conversionMap = {}) override { ExecutionManagementService::extractOrderInfo(element, x, extractionFieldNameMap); { - auto it1 = x.FindMember("accFillSz"); - auto it2 = x.FindMember("avgPx"); + auto it1 = x.FindMember("accBaseVolume"); + auto it2 = x.FindMember("priceAvg"); if (it1 != x.MemberEnd() && it2 != x.MemberEnd()) { auto it1Str = std::string(it1->value.GetString()); auto it2Str = std::string(it2->value.GetString()); @@ -332,17 +346,27 @@ class ExecutionManagementServiceBitgetFutures : public ExecutionManagementServic const auto& instrumentSet = subscription.getInstrumentSet(); const auto& instrumentType = subscription.getInstrumentType(); for (const auto& field : fieldSet) { - std::string channel; - if (fieldSet.find(CCAPI_EM_ORDER_UPDATE) != fieldSet.end() || fieldSet.find(CCAPI_EM_PRIVATE_TRADE) != fieldSet.end()) { - channel = "orders"; - } - for (const auto& instrument : instrumentSet) { - rj::Value arg(rj::kObjectType); - arg.AddMember("channel", rj::Value(channel.c_str(), allocator).Move(), allocator); - arg.AddMember("instId", rj::Value(instrument.c_str(), allocator).Move(), allocator); - arg.AddMember("instType", rj::Value(instrumentType.c_str(), allocator).Move(), allocator); - args.PushBack(arg, allocator); - } + if (field == CCAPI_EM_ORDER_UPDATE || field == CCAPI_EM_PRIVATE_TRADE) { + for (const auto& instrument : instrumentSet) { + rj::Value arg(rj::kObjectType); + arg.AddMember("channel", rj::Value("orders", allocator).Move(), allocator); + arg.AddMember("instId", rj::Value(instrument.c_str(), allocator).Move(), allocator); + arg.AddMember("instType", rj::Value(instrumentType.c_str(), allocator).Move(), allocator); + args.PushBack(arg, allocator); + } + } else if (field == CCAPI_EM_POSITION_UPDATE) { + rj::Value arg(rj::kObjectType); + arg.AddMember("channel", rj::Value("positions", allocator).Move(), allocator); + arg.AddMember("instId", rj::Value("default", allocator).Move(), allocator); + arg.AddMember("instType", rj::Value(instrumentType.c_str(), allocator).Move(), allocator); + args.PushBack(arg, allocator); + } else if (field == CCAPI_EM_BALANCE_UPDATE) { + rj::Value arg(rj::kObjectType); + arg.AddMember("channel", rj::Value("account", allocator).Move(), allocator); + arg.AddMember("coin", rj::Value("default", allocator).Move(), allocator); + arg.AddMember("instType", rj::Value(instrumentType.c_str(), allocator).Move(), allocator); + args.PushBack(arg, allocator); + } } document.AddMember("args", args, allocator); rj::StringBuffer stringBufferSubscribe; @@ -390,8 +414,9 @@ class ExecutionManagementServiceBitgetFutures : public ExecutionManagementServic const rj::Value& arg = document["arg"]; std::string channel = std::string(arg["channel"].GetString()); event.setType(Event::Type::SUBSCRIPTION_DATA); - std::string instrument = arg["instId"].GetString(); - if (instrumentSet.empty() || instrumentSet.find(instrument) != instrumentSet.end()) { + std::string instId = (channel == "account") ? "coin" : "instId"; + std::string instrument = arg[instId.c_str()].GetString(); + if (instrumentSet.empty() || instrument == "default" || instrumentSet.find(instrument) != instrumentSet.end()) { if (channel == "orders") { if (fieldSet.find(CCAPI_EM_PRIVATE_TRADE) != fieldSet.end()) { const rj::Value& data = document["data"]; @@ -407,15 +432,19 @@ class ExecutionManagementServiceBitgetFutures : public ExecutionManagementServic std::vector elementList; Element element; element.insert(CCAPI_TRADE_ID, tradeId); - element.insert(CCAPI_EM_ORDER_LAST_EXECUTED_PRICE, std::string(x["fillPx"].GetString())); - element.insert(CCAPI_EM_ORDER_LAST_EXECUTED_SIZE, std::string(x["fillSz"].GetString())); + element.insert(CCAPI_EM_ORDER_LAST_EXECUTED_PRICE, std::string(x["fillPrice"].GetString())); + element.insert(CCAPI_EM_ORDER_LAST_EXECUTED_SIZE, std::string(x["baseVolume"].GetString())); element.insert(CCAPI_EM_ORDER_SIDE, std::string(x["side"].GetString()) == "buy" ? CCAPI_EM_ORDER_SIDE_BUY : CCAPI_EM_ORDER_SIDE_SELL); - element.insert(CCAPI_IS_MAKER, std::string(x["execType"].GetString()) == "M" ? "1" : "0"); - element.insert(CCAPI_EM_ORDER_ID, std::string(x["ordId"].GetString())); - element.insert(CCAPI_EM_CLIENT_ORDER_ID, std::string(x["clOrdId"].GetString())); + element.insert(CCAPI_IS_MAKER, std::string(x["tradeScope"].GetString()) == "M" ? "1" : "0"); + element.insert(CCAPI_EM_ORDER_ID, std::string(x["orderId"].GetString())); + auto itClientOId = x.FindMember("clientOId"); + std::string clientOId = itClientOId != x.MemberEnd() ? itClientOId->value.GetString() : ""; + if(!clientOId.empty()){ + element.insert(CCAPI_EM_CLIENT_ORDER_ID, clientOId); + } element.insert(CCAPI_EM_ORDER_INSTRUMENT, instrument); element.insert(CCAPI_EM_ORDER_FEE_QUANTITY, std::string(x["fillFee"].GetString())); - element.insert(CCAPI_EM_ORDER_FEE_ASSET, std::string(x["fillFeeCcy"].GetString())); + element.insert(CCAPI_EM_ORDER_FEE_ASSET, std::string(x["fillFeeCoin"].GetString())); elementList.emplace_back(std::move(element)); message.setElementList(elementList); messageList.emplace_back(std::move(message)); @@ -431,12 +460,12 @@ class ExecutionManagementServiceBitgetFutures : public ExecutionManagementServic message.setTime(UtilTime::makeTimePointFromMilliseconds(std::stoll(std::string(x["uTime"].GetString())))); message.setType(Message::Type::EXECUTION_MANAGEMENT_EVENTS_ORDER_UPDATE); const std::map>& extractionFieldNameMap = { - {CCAPI_EM_ORDER_ID, std::make_pair("ordId", JsonDataType::STRING)}, - {CCAPI_EM_CLIENT_ORDER_ID, std::make_pair("clOrdId", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_ID, std::make_pair("orderId", JsonDataType::STRING)}, + {CCAPI_EM_CLIENT_ORDER_ID, std::make_pair("clientOId", JsonDataType::STRING)}, {CCAPI_EM_ORDER_SIDE, std::make_pair("side", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_LIMIT_PRICE, std::make_pair("px", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_QUANTITY, std::make_pair("sz", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_CUMULATIVE_FILLED_QUANTITY, std::make_pair("accFillSz", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_LIMIT_PRICE, std::make_pair("price", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_QUANTITY, std::make_pair("size", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_CUMULATIVE_FILLED_QUANTITY, std::make_pair("accBaseVolume", JsonDataType::STRING)}, {CCAPI_EM_ORDER_STATUS, std::make_pair("status", JsonDataType::STRING)}, }; Element info; @@ -448,6 +477,42 @@ class ExecutionManagementServiceBitgetFutures : public ExecutionManagementServic messageList.emplace_back(std::move(message)); } } + } else if (channel == "positions"){ + const rj::Value& data = document["data"]; + for (const auto& x : data.GetArray()) { + auto time = TimePoint(std::chrono::milliseconds(std::stoll(x["uTime"].GetString()))); + Message message; + message.setTimeReceived(timeReceived); + message.setCorrelationIdList({subscription.getCorrelationId()}); + message.setTime(time); + message.setType(Message::Type::EXECUTION_MANAGEMENT_EVENTS_POSITION_UPDATE); + std::vector elementList; + Element element; + element.insert(CCAPI_INSTRUMENT, x["instId"].GetString()); + element.insert(CCAPI_EM_POSITION_SIDE, x["holdSide"].GetString()); + element.insert(CCAPI_EM_POSITION_QUANTITY, x["total"].GetString()); + element.insert(CCAPI_EM_POSITION_ENTRY_PRICE, x["openPriceAvg"].GetString()); + element.insert(CCAPI_EM_POSITION_LEVERAGE, x["leverage"].GetString()); + elementList.emplace_back(std::move(element)); + message.setElementList(elementList); + messageList.emplace_back(std::move(message)); + } + } else if (channel == "account"){ + const rj::Value& data = document["data"]; + for (const auto& x : data.GetArray()) { + Message message; + message.setTimeReceived(timeReceived); + message.setCorrelationIdList({subscription.getCorrelationId()}); + message.setType(Message::Type::EXECUTION_MANAGEMENT_EVENTS_BALANCE_UPDATE); + std::vector elementList; + Element element; + element.insert(CCAPI_EM_ASSET, x["marginCoin"].GetString()); + element.insert(CCAPI_EM_QUANTITY_TOTAL, x["equity"].GetString()); + element.insert(CCAPI_EM_QUANTITY_AVAILABLE_FOR_TRADING, x["available"].GetString()); + elementList.emplace_back(std::move(element)); + message.setElementList(elementList); + messageList.emplace_back(std::move(message)); + } } } } else if (eventStr == "subscribe") {