| | |
| | | |
| | | #include "book.hpp" |
| | | #include "cpp-httplib/httplib.h" |
| | | #include "easywsclient/easywsclient.hpp" |
| | | #include "json.hpp" |
| | | #include "secrets.hpp" |
| | | |
| | | #include <chrono> |
| | | #include <cstddef> |
| | | #include <iostream> |
| | | #include <memory> |
| | | #include <queue> |
| | | #include <sstream> |
| | | #include <stdexcept> |
| | | #include <string> |
| | | #include <unordered_map> |
| | | |
| | | using namespace std::literals; |
| | | |
| | | namespace protocol |
| | | { |
| | | static std::unordered_map<json::MessageTypes, book::ProductTypeEnum> |
| | | mapAnnounce; |
| | | std::string server = std::string(HOST) + ":" + std::string(PORT); |
| | | httplib::Client cli("http://" + server); |
| | | mapAnnounce; |
| | | #define SERVER HOST ":" PORT |
| | | httplib::Client cli("http://" SERVER); |
| | | std::unique_ptr<easywsclient::WebSocket> ws; |
| | | |
| | | double lastime = 0; |
| | | |
| | | void initialise() |
| | | { |
| | | mapAnnounce = {{json::FUTURE_TYPE, book::FUTURE}, |
| | | {json::SPREAD_TYPE, book::SPREAD}, |
| | | {json::CALL_TYPE, book::CALL}, |
| | | {json::PUT_TYPE, book::PUT}}; |
| | | mapAnnounce = {{json::FUTURE_TYPE, book::FUTURE}, |
| | | {json::SPREAD_TYPE, book::SPREAD}, |
| | | {json::CALL_TYPE, book::CALL}, |
| | | {json::PUT_TYPE, book::PUT}}; |
| | | } |
| | | |
| | | std::unordered_map<std::string, book::Book> recoverBook() |
| | | { |
| | | std::unordered_map<std::string, book::Book> bs; |
| | | std::ifstream sampleFile("../rec.log"); |
| | | std::stringstream ss; |
| | | ss << sampleFile.rdbuf(); |
| | | // httplib::Client cli("http://" + server); |
| | | // auto res = cli.Get("/recover"); |
| | | std::string l; |
| | | l = ss.str(); |
| | | std::queue<json::Message*> a(json::parseMany(l)); |
| | | while (!a.empty()) { |
| | | protocol::handleMessage(bs, a.front()); |
| | | delete a.front(); |
| | | a.pop(); |
| | | } |
| | | return bs; |
| | | std::unordered_map<std::string, book::Book> bs; |
| | | // std::ifstream sampleFile("../data.test"); |
| | | // std::stringstream ss; |
| | | // ss << sampleFile.rdbuf(); |
| | | auto res = cli.Get("/recover"); |
| | | std::string l; |
| | | // l = ss.str(); |
| | | l = res->body; |
| | | std::queue<json::Message*> a(json::parse(l)); |
| | | while (!a.empty()) { |
| | | if (static_cast<json::FromExchange*>(a.front()) != nullptr || |
| | | static_cast<json::FromExchange*>(a.front())->timestamp > lastime) { |
| | | lastime = static_cast<json::FromExchange*>(a.front())->timestamp; |
| | | protocol::handleMessage(bs, a.front()); |
| | | } |
| | | delete a.front(); |
| | | a.pop(); |
| | | } |
| | | return bs; |
| | | } |
| | | |
| | | void addOrder(json::AddMessage& order) |
| | | void createWebSocket() |
| | | { |
| | | std::string message = "{\"message\": " + order.as_string() + ", " + |
| | | "\"username\": \"" + std::string(USER) + |
| | | "\", \"password\": \"" + std::string(PASS) + "\"}"; |
| | | send(message); |
| | | ws = std::unique_ptr<easywsclient::WebSocket>( |
| | | easywsclient::WebSocket::pointer( |
| | | easywsclient::WebSocket::from_url("ws://" SERVER "/information"))); |
| | | ws->poll(); |
| | | } |
| | | |
| | | void deleteOrder(json::DeleteMessage& order) |
| | | std::deque<json::Message*> |
| | | catchUp(std::unordered_map<std::string, book::Book>& bs) |
| | | { |
| | | std::string message = "{\"message\": " + order.as_string() + |
| | | ", \"username\": \"" + std::string(USER) + |
| | | "\", \"password\": \"" + std::string(PASS) + "\"}"; |
| | | send(message); |
| | | std::string feed; |
| | | bool gotMessage; |
| | | std::deque<json::Message*> out; |
| | | do { |
| | | gotMessage = false; |
| | | ws->poll(); |
| | | ws->dispatch([gotMessageOut = &gotMessage, messageOut = &feed, |
| | | ws = ws.get()](const std::string& message) { |
| | | *gotMessageOut = true; |
| | | *messageOut = message; |
| | | }); |
| | | if (gotMessage) { |
| | | std::queue<json::Message*> a(json::parse(feed)); |
| | | while (!a.empty()) { |
| | | if (static_cast<json::FromExchange*>(a.front()) != nullptr || |
| | | static_cast<json::FromExchange*>(a.front())->timestamp > |
| | | lastime) { |
| | | lastime = |
| | | static_cast<json::FromExchange*>(a.front())->timestamp; |
| | | } |
| | | protocol::handleMessage(bs, a.front()); |
| | | out.push_back(a.front()); |
| | | a.pop(); |
| | | } |
| | | } |
| | | } while (gotMessage); |
| | | return out; |
| | | } |
| | | |
| | | json::Message* addOrder(json::AddMessage& order) |
| | | { |
| | | std::string message = order.as_string(); |
| | | return send(message); |
| | | } |
| | | |
| | | json::Message* deleteOrder(json::DeleteMessage& order) |
| | | { |
| | | std::string message = order.as_string(); |
| | | return send(message); |
| | | } |
| | | |
| | | void handleMessage(std::unordered_map<std::string, book::Book>& bs, |
| | | json::Message* message) |
| | | { |
| | | if (mapAnnounce.empty()) initialise(); |
| | | json::AnnounceMessage* a = dynamic_cast<json::AnnounceMessage*>(message); |
| | | json::SettleMessage* b = dynamic_cast<json::SettleMessage*>(message); |
| | | json::AddedMessage* c = dynamic_cast<json::AddedMessage*>(message); |
| | | json::DeletedMessage* d = dynamic_cast<json::DeletedMessage*>(message); |
| | | json::TradeMessage* e = dynamic_cast<json::TradeMessage*>(message); |
| | | json::Broker* f = dynamic_cast<json::Broker*>(message); |
| | | switch (message->type) { |
| | | case json::FUTURE_TYPE: |
| | | case json::SPREAD_TYPE: |
| | | case json::CALL_TYPE: |
| | | case json::PUT_TYPE: |
| | | announce(bs, a); |
| | | break; |
| | | case json::SETTLEMENT: |
| | | settle(bs, dynamic_cast<json::SettleMessage*>(message)); |
| | | break; |
| | | case json::ADDED: |
| | | addedOrder(bs, dynamic_cast<json::AddedMessage*>(message)); |
| | | break; |
| | | case json::DELETED: |
| | | deletedOrder(bs, dynamic_cast<json::DeletedMessage*>(message)); |
| | | break; |
| | | case json::TRADE: |
| | | tradeOrder(bs, dynamic_cast<json::TradeMessage*>(message)); |
| | | break; |
| | | case json::BROKER_REQUEST: |
| | | case json::BROKER_ACK: |
| | | case json::BROKER_CONFIRM: |
| | | broker(bs, dynamic_cast<json::Broker*>(message)); |
| | | break; |
| | | default:; |
| | | } |
| | | if (mapAnnounce.empty()) initialise(); |
| | | if (dynamic_cast<json::FromExchange*>(message) != nullptr) { |
| | | lastime = dynamic_cast<json::FromExchange*>(message)->timestamp; |
| | | } |
| | | switch (message->type) { |
| | | case json::FUTURE_TYPE: |
| | | case json::SPREAD_TYPE: |
| | | case json::CALL_TYPE: |
| | | case json::PUT_TYPE: |
| | | announce(bs, static_cast<json::AnnounceMessage*>(message)); |
| | | break; |
| | | case json::SETTLEMENT: |
| | | settle(bs, static_cast<json::SettleMessage*>(message)); |
| | | break; |
| | | case json::ADDED: |
| | | addedOrder(bs, static_cast<json::AddedMessage*>(message)); |
| | | break; |
| | | case json::DELETED: |
| | | deletedOrder(bs, static_cast<json::DeletedMessage*>(message)); |
| | | break; |
| | | case json::TRADE: |
| | | tradeOrder(bs, static_cast<json::TradeMessage*>(message)); |
| | | break; |
| | | case json::BROKER_REQUEST: |
| | | case json::BROKER_ACK: |
| | | case json::BROKER_CONFIRM: |
| | | broker(bs, static_cast<json::Broker*>(message)); |
| | | break; |
| | | default:; |
| | | } |
| | | } |
| | | |
| | | void announce(std::unordered_map<std::string, book::Book>& bs, |
| | | json::AnnounceMessage* message) |
| | | { |
| | | bs[message->product] = |
| | | book::Book(mapAnnounce[message->type], message->product, |
| | | message->stationId, message->unit, message->expiry, |
| | | message->aggFee, message->pasFee, message->broFee); |
| | | bs[message->product] = |
| | | book::Book(mapAnnounce[message->type], message->product, |
| | | message->stationId, message->unit, message->expiry, |
| | | message->aggFee, message->pasFee, message->broFee); |
| | | } |
| | | void settle(std::unordered_map<std::string, book::Book>& bs, |
| | | json::SettleMessage* message) |
| | | { |
| | | bs.erase(message->product); |
| | | bs.erase(message->product); |
| | | } |
| | | void addedOrder(std::unordered_map<std::string, book::Book>& bs, |
| | | json::AddedMessage* message) |
| | | { |
| | | if (message->side == book::Buy) { |
| | | book::Order t(message->price, book::Buy, message->resting, |
| | | message->timestamp, message->id); |
| | | bs[message->product].bid(t); |
| | | } else { |
| | | book::Order t(message->price, book::Sell, message->resting, |
| | | message->timestamp, message->id); |
| | | bs[message->product].ask(t); |
| | | } |
| | | if (message->side == book::Buy && message->resting) { |
| | | book::Order t(message->price, book::Buy, message->resting, |
| | | message->timestamp, message->id); |
| | | bs[message->product].bid(t); |
| | | } else if (message->side == book::Sell && message->resting) { |
| | | book::Order t(message->price, book::Sell, message->resting, |
| | | message->timestamp, message->id); |
| | | bs[message->product].ask(t); |
| | | } |
| | | } |
| | | void deletedOrder(std::unordered_map<std::string, book::Book>& bs, |
| | | json::DeletedMessage* message) |
| | | { |
| | | if (message->side == book::Buy) { |
| | | for (auto i = bs[message->product].bidSide.begin(); |
| | | i != bs[message->product].bidSide.end(); i++) { |
| | | if (i->id == message->id) { |
| | | bs[message->product].bidSide.erase(i); |
| | | std::make_heap(bs[message->product].bidSide.begin(), |
| | | bs[message->product].bidSide.end(), |
| | | std::less<book::Level>()); |
| | | } |
| | | if (message->side == book::Buy) { |
| | | for (auto i = bs[message->product].bidSide.begin(); |
| | | i != bs[message->product].bidSide.end(); i++) { |
| | | if (i->id == message->id) { |
| | | bs[message->product].bidSide.erase(i); |
| | | break; |
| | | } |
| | | } |
| | | } else { |
| | | for (auto i = bs[message->product].askSide.begin(); |
| | | i != bs[message->product].askSide.end(); i++) { |
| | | if (i->id == message->id) { |
| | | bs[message->product].askSide.erase(i); |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | } else { |
| | | for (auto i = bs[message->product].askSide.begin(); |
| | | i != bs[message->product].askSide.end(); i++) { |
| | | if (i->id == message->id) { |
| | | bs[message->product].askSide.erase(i); |
| | | std::make_heap(bs[message->product].askSide.begin(), |
| | | bs[message->product].askSide.end(), |
| | | std::greater<book::Level>()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | void tradeOrder(std::unordered_map<std::string, book::Book>& bs, |
| | | json::TradeMessage* message) |
| | | { |
| | | if (message->tradeType == json::BUY_AGGRESSOR) { |
| | | book::Order t(message->price, book::Buy, message->volume, |
| | | message->timestamp, message->passiveOrder); |
| | | bs[message->product].bid(t); |
| | | } else { |
| | | book::Order t(message->price, book::Sell, message->volume, |
| | | message->timestamp, message->passiveOrder); |
| | | bs[message->product].ask(t); |
| | | } |
| | | if (message->tradeType == json::BUY_AGGRESSOR) { |
| | | if (message->passiveOrderRemaining > 0) { |
| | | for (auto& i : bs[message->product].askSide) { |
| | | if (i.id == message->passiveOrder) { |
| | | i.volume = message->passiveOrderRemaining; |
| | | break; |
| | | } |
| | | } |
| | | } else { |
| | | for (auto i = bs[message->product].askSide.begin(); |
| | | i != bs[message->product].askSide.end(); i++) { |
| | | if (i->id == message->passiveOrder) { |
| | | bs[message->product].askSide.erase(i); |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | } else if (message->tradeType == json::SELL_AGGRESSOR) { |
| | | if (message->passiveOrderRemaining > 0) { |
| | | for (auto& i : bs[message->product].bidSide) { |
| | | if (i.id == message->passiveOrder) { |
| | | i.volume = message->passiveOrderRemaining; |
| | | break; |
| | | } |
| | | } |
| | | } else { |
| | | for (auto i = bs[message->product].bidSide.begin(); |
| | | i != bs[message->product].bidSide.end(); i++) { |
| | | if (i->id == message->passiveOrder) { |
| | | bs[message->product].bidSide.erase(i); |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | void broker(std::unordered_map<std::string, book::Book>& bs, |
| | | json::Broker* message) |
| | | { |
| | | } |
| | | |
| | | void send(std::string& message) |
| | | json::Message* send(std::string& message) |
| | | { |
| | | cli.Post("/execution", message, "text/plain"); |
| | | httplib::MultipartFormDataItems a = {{"message", message, "", ""}, |
| | | {"username", USER, "", ""}, |
| | | {"password", PASS, "", ""}}; |
| | | auto res = |
| | | cli.Post("/execution", |
| | | "message=" + message + "&username=" USER "&password=" PASS, |
| | | "application/x-www-form-urlencoded"); |
| | | std::string b = res->body; |
| | | std::queue<json::Message*> c = json::parse(b); |
| | | return c.front(); |
| | | } |
| | | |
| | | } // namespace protocol |