|
| 1 | +#include <atomic> |
| 2 | + |
| 3 | +#include "ccapi_cpp/ccapi_session.h" |
| 4 | + |
| 5 | +namespace ccapi { |
| 6 | +Logger* Logger::logger = nullptr; // This line is needed. |
| 7 | + |
| 8 | +class MyEventHandler : public EventHandler { |
| 9 | + public: |
| 10 | + MyEventHandler(const std::string& symbol, const std::string& side, const std::string& quantity, const std::string& price, int clientOrderIdLength, |
| 11 | + bool cancelByClientOrderId, int numOrders) |
| 12 | + : symbol(symbol), |
| 13 | + side(side), |
| 14 | + quantity(quantity), |
| 15 | + price(price), |
| 16 | + clientOrderIdLength(clientOrderIdLength), |
| 17 | + cancelByClientOrderId(cancelByClientOrderId), |
| 18 | + numOrders(numOrders) {} |
| 19 | + |
| 20 | + bool processEvent(const Event& event, Session* sessionPtr) override { |
| 21 | + if (event.getType() == Event::Type::SUBSCRIPTION_STATUS) { |
| 22 | + const auto& message = event.getMessageList().at(0); |
| 23 | + if (message.getType() == Message::Type::SUBSCRIPTION_STARTED) { |
| 24 | + Request request(Request::Operation::GET_OPEN_ORDERS, "okx", this->symbol); |
| 25 | + sessionPtr->sendRequest(request); |
| 26 | + } |
| 27 | + } else if (event.getType() == Event::Type::RESPONSE) { |
| 28 | + const auto& message = event.getMessageList().at(0); |
| 29 | + if (message.getType() == Message::Type::GET_OPEN_ORDERS) { |
| 30 | + for (int i = 0; i < this->numOrders; ++i) { |
| 31 | + sessionPtr->setTimer( |
| 32 | + std::to_string(i), 100 * i, |
| 33 | + [](const boost::system::error_code&) { |
| 34 | + std::cout << std::string("Timer error handler is triggered at ") + UtilTime::getISOTimestamp(UtilTime::now()) << std::endl; |
| 35 | + }, |
| 36 | + [this, sessionPtr]() { |
| 37 | + Request request(Request::Operation::CREATE_ORDER, "okx", this->symbol); |
| 38 | + const auto& clientOrderId = UtilString::generateRandomString(this->clientOrderIdLength); |
| 39 | + request.appendParam({ |
| 40 | + {"SIDE", UtilString::toUpper(this->side)}, |
| 41 | + {"LIMIT_PRICE", this->price}, |
| 42 | + {"QUANTITY", this->quantity}, |
| 43 | + {"CLIENT_ORDER_ID", clientOrderId}, |
| 44 | + }); |
| 45 | + this->orderCreateTimes.emplace(clientOrderId, UtilTime::now()); |
| 46 | + sessionPtr->sendRequest(request); |
| 47 | + }); |
| 48 | + } |
| 49 | + } |
| 50 | + } else if (event.getType() == Event::Type::SUBSCRIPTION_DATA) { |
| 51 | + const auto& message = event.getMessageList().at(0); |
| 52 | + if (message.getType() == Message::Type::EXECUTION_MANAGEMENT_EVENTS_ORDER_UPDATE) { |
| 53 | + const auto& now = UtilTime::now(); |
| 54 | + const auto& element = message.getElementList().front(); |
| 55 | + const auto& orderId = element.getValue("ORDER_ID"); |
| 56 | + const auto& clientOrderId = element.getValue("CLIENT_ORDER_ID"); |
| 57 | + const auto& status = element.getValue("STATUS"); |
| 58 | + if (status == "live") { |
| 59 | + this->orderCreateLatencies.push_back(now - this->orderCreateTimes.at(clientOrderId)); |
| 60 | + this->orderCreateTimes.erase(clientOrderId); |
| 61 | + Request request(Request::Operation::CANCEL_ORDER, "okx", this->symbol); |
| 62 | + if (this->cancelByClientOrderId) { |
| 63 | + request.appendParam({ |
| 64 | + {"CLIENT_ORDER_ID", clientOrderId}, |
| 65 | + }); |
| 66 | + } else { |
| 67 | + request.appendParam({ |
| 68 | + {"ORDER_ID", orderId}, |
| 69 | + }); |
| 70 | + } |
| 71 | + this->orderCancelTimes.emplace(clientOrderId, now); |
| 72 | + sessionPtr->sendRequest(request); |
| 73 | + } else if (status == "canceled") { |
| 74 | + this->orderCancelLatencies.push_back(now - this->orderCancelTimes.at(clientOrderId)); |
| 75 | + this->orderCancelTimes.erase(clientOrderId); |
| 76 | + ++this->numCanceledOrders; |
| 77 | + if (this->numCanceledOrders == this->numOrders) { |
| 78 | + done = true; |
| 79 | + } |
| 80 | + } |
| 81 | + } |
| 82 | + } |
| 83 | + return true; |
| 84 | + } |
| 85 | + |
| 86 | + std::string symbol; |
| 87 | + std::string side; |
| 88 | + std::string quantity; |
| 89 | + std::string price; |
| 90 | + int clientOrderIdLength{}; |
| 91 | + bool cancelByClientOrderId{}; |
| 92 | + int numOrders{}; |
| 93 | + |
| 94 | + std::map<std::string, TimePoint> orderCreateTimes; |
| 95 | + std::vector<std::chrono::nanoseconds> orderCreateLatencies; |
| 96 | + std::map<std::string, TimePoint> orderCancelTimes; |
| 97 | + std::vector<std::chrono::nanoseconds> orderCancelLatencies; |
| 98 | + |
| 99 | + int numCanceledOrders{}; |
| 100 | + std::atomic<bool> done{}; |
| 101 | +}; |
| 102 | +} /* namespace ccapi */ |
| 103 | + |
| 104 | +using ::ccapi::MyEventHandler; |
| 105 | +using ::ccapi::Request; |
| 106 | +using ::ccapi::Session; |
| 107 | +using ::ccapi::SessionConfigs; |
| 108 | +using ::ccapi::SessionOptions; |
| 109 | +using ::ccapi::Subscription; |
| 110 | +using ::ccapi::UtilSystem; |
| 111 | + |
| 112 | +int main(int argc, char** argv) { |
| 113 | + if (UtilSystem::getEnvAsString("OKX_API_KEY").empty()) { |
| 114 | + std::cerr << "Please set environment variable OKX_API_KEY" << std::endl; |
| 115 | + return EXIT_FAILURE; |
| 116 | + } |
| 117 | + if (UtilSystem::getEnvAsString("OKX_API_SECRET").empty()) { |
| 118 | + std::cerr << "Please set environment variable OKX_API_SECRET" << std::endl; |
| 119 | + return EXIT_FAILURE; |
| 120 | + } |
| 121 | + if (UtilSystem::getEnvAsString("OKX_API_PASSPHRASE").empty()) { |
| 122 | + std::cerr << "Please set environment variable OKX_API_PASSPHRASE" << std::endl; |
| 123 | + return EXIT_FAILURE; |
| 124 | + } |
| 125 | + const auto& symbol = UtilSystem::getEnvAsString("SYMBOL"); |
| 126 | + const auto& side = UtilSystem::getEnvAsString("SIDE"); |
| 127 | + const auto& quantity = UtilSystem::getEnvAsString("QUANTITY"); |
| 128 | + const auto& price = UtilSystem::getEnvAsString("PRICE"); |
| 129 | + const auto& clientOrderIdLength = UtilSystem::getEnvAsInt("CLIENT_ORDER_ID_LENGTH", 4); |
| 130 | + const auto& cancelByClientOrderId = UtilSystem::getEnvAsBool("CANCEL_BY_CLIENT_ORDER_ID"); |
| 131 | + const auto& numOrders = UtilSystem::getEnvAsInt("NUM_ORDERS", 10); |
| 132 | + SessionOptions sessionOptions; |
| 133 | + SessionConfigs sessionConfigs; |
| 134 | + MyEventHandler eventHandler(symbol, side, quantity, price, clientOrderIdLength, cancelByClientOrderId, numOrders); |
| 135 | + Session session(sessionOptions, sessionConfigs, &eventHandler); |
| 136 | + Subscription subscription("okx", symbol, "ORDER_UPDATE"); |
| 137 | + session.subscribe(subscription); |
| 138 | + while (!eventHandler.done) { |
| 139 | + std::this_thread::sleep_for(std::chrono::seconds(1)); |
| 140 | + } |
| 141 | + session.stop(); |
| 142 | + double avgCreateLatencyMs = |
| 143 | + std::accumulate(eventHandler.orderCreateLatencies.begin(), eventHandler.orderCreateLatencies.end(), std::chrono::nanoseconds{0}).count() / 1e6 / |
| 144 | + eventHandler.orderCreateLatencies.size(); |
| 145 | + std::cout << "avgCreateLatencyMs = " << avgCreateLatencyMs << " for " << eventHandler.orderCreateLatencies.size() << " orders" << std::endl; |
| 146 | + double avgCancelLatencyMs = |
| 147 | + std::accumulate(eventHandler.orderCancelLatencies.begin(), eventHandler.orderCancelLatencies.end(), std::chrono::nanoseconds{0}).count() / 1e6 / |
| 148 | + eventHandler.orderCancelLatencies.size(); |
| 149 | + std::cout << "avgCancelLatencyMs = " << avgCancelLatencyMs << " for " << eventHandler.orderCancelLatencies.size() << " orders" << std::endl; |
| 150 | + return EXIT_SUCCESS; |
| 151 | +} |
0 commit comments