From 16b655e7c8cfb2e32e6bb839373f30ad63506f9a Mon Sep 17 00:00:00 2001 From: Joel Grunbaum <joelgrun@gmail.com> Date: Sat, 08 Jan 2022 13:28:32 +0000 Subject: [PATCH] Added json parsing, protocol comprehension and began executables for algs --- date | 1 .gitignore | 1 main.cpp | 10 .gitmodules | 6 .clang-format | 6 book.hpp | 87 +- json.cpp | 493 ++++++++++++++++++++ protocol.cpp | 177 +++++++ CMakeLists.txt | 18 /dev/null | 16 strat.cpp | 5 click.cpp | 77 +++ test.cpp | 15 book.cpp | 255 +++++----- json.hpp | 169 +++++++ strat.hpp | 7 secrets.hpp | 14 bot.cpp | 6 cpp-httplib | 1 protocol.hpp | 37 + 20 files changed, 1,206 insertions(+), 195 deletions(-) diff --git a/.clang-format b/.clang-format index e6786f7..68225bd 100644 --- a/.clang-format +++ b/.clang-format @@ -5,4 +5,8 @@ AllowShortIfStatementsOnASingleLine: true BreakBeforeBraces: Linux IndentCaseLabels: false ---- \ No newline at end of file +PointerAlignment: Left +AllowShortIfStatementsOnASingleLine: true +IndentCaseBlocks: true +IndentCaseLabels: false +SortUsingDeclarations: true \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4e07316..8c4231d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ bid ask oneshot +build \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d553919 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "cpp-httplib"] + path = cpp-httplib + url = https://github.com/yhirose/cpp-httplib.git +[submodule "date"] + path = date + url = https://github.com/HowardHinnant/date.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..1c49f41 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.10) + +project(bomex) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +add_subdirectory(date) + +add_library(MAIN json.cpp date protocol.cpp book.cpp) + +add_executable(test test.cpp strat.cpp) +add_executable(bot bot.cpp strat.cpp) +add_executable(click click.cpp) + +target_link_libraries(test PUBLIC MAIN) +target_link_libraries(bot PUBLIC MAIN) +target_link_libraries(click PUBLIC MAIN) diff --git a/Makefile b/Makefile deleted file mode 100644 index 96fba56..0000000 --- a/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -CC=gcc -CXX=g++ -CXXFLAGS=-g -Wall -std=c++20 - -.PHONY: default all -default: all - -all: exec - -exec: main.o book.o - ${CXX} -o $@ $^ - -%: %.cpp %.hpp - ${CXX} ${CXXFLAGS} -o $@ $^ - - diff --git a/book.cpp b/book.cpp index 9cb14e7..4702e4d 100644 --- a/book.cpp +++ b/book.cpp @@ -1,95 +1,84 @@ #include "book.hpp" +#include <algorithm> #include <chrono> #include <cstddef> #include <iostream> -Order::Order(double price, OrderSideEnum side, int volume, - std::chrono::nanoseconds timestamp, std::string id) +namespace book +{ +Order::Order(double price, OrderSideEnum side, int volume, double timestamp, + std::string id) : price{price}, side{side}, remaining_volume{volume}, - filled_volume(0), timestamp{timestamp}, id{id} {} + filled_volume(0), timestamp{timestamp}, id{id} +{ +} -Level::Level(Order &order) +Level::Level(Order& order) : price{order.price}, volume(order.remaining_volume), side{order.side}, - timestamp{order.timestamp}, id{order.id} {} - -bool operator<(const Level &a, const Level &b) { - if (a.price < b.price) - return true; - else if (a.price == b.price && (a.timestamp.count() > b.timestamp.count())) - return true; - else - return false; + timestamp{order.timestamp}, id{order.id} +{ } -bool operator>(const Level &a, const Level &b) { - if (a.price > b.price) - return true; - else if (a.price == b.price && (a.timestamp.count() > b.timestamp.count())) - return true; - else - return false; +bool operator<(const Level& a, const Level& b) +{ + if (a.price < b.price) + return true; + else if (a.price == b.price && (a.timestamp > b.timestamp)) + return true; + else + return false; } -bool operator<=(const Level &a, const Level &b) { - if (a.price <= b.price) - return true; - else if (a.price == b.price && (a.timestamp.count() >= b.timestamp.count())) - return true; - else - return false; +bool operator>(const Level& a, const Level& b) +{ + if (a.price > b.price) + return true; + else if (a.price == b.price && (a.timestamp > b.timestamp)) + return true; + else + return false; } -bool operator>=(const Level &a, const Level &b) { - if (a.price >= b.price) - return true; - else if (a.price == b.price && (a.timestamp.count() >= b.timestamp.count())) - return true; - else - return false; +bool operator<=(const Level& a, const Level& b) +{ + if (a.price <= b.price) + return true; + else if (a.price == b.price && (a.timestamp >= b.timestamp)) + return true; + else + return false; } -bool operator==(const Level &a, const Level &b) { - if (a.price == b.price && (a.timestamp.count() == b.timestamp.count())) - return true; - else - return false; +bool operator>=(const Level& a, const Level& b) +{ + if (a.price >= b.price) + return true; + else if (a.price == b.price && (a.timestamp >= b.timestamp)) + return true; + else + return false; +} + +bool operator==(const Level& a, const Level& b) +{ + if (a.price == b.price && (a.timestamp == b.timestamp)) + return true; + else + return false; } std::ostream& operator<<(std::ostream& out, const Level& a) { - return out << "Price: " << a.price << ", volume: " << a.volume << ", time: " << a.timestamp.count() << ", id: " << a.id; -} - -template<class T> -void Side<T>::deleteLevel(std::string orderId) -{ - for (auto i = this->c.begin(); i != this->c.end();) { - if (*i.id == orderId) { - this->c.erase(i); - std::make_heap(this->c.begin(), this->c.end(), this->comp); - } - } -} - -template<class T> -void Side<T>::topRemoveVolume(int volume) { this->c[0].volume -= volume; } - -template<class T> -void Side<T>::printTop(std::size_t num) -{ - std::vector<Level> copy(this->c); - std::sort(copy.begin(), copy.end(), this->comp); - if (copy.size() && copy[0].side == Buy) - std::reverse(copy.begin(), copy.end()); - for (std::size_t i = 0; i < copy.size() && i < num; i++) { - std::cout << copy[i] << std::endl; - } + return out << "Price: " << a.price << ", volume: " << a.volume + << ", time: " << a.timestamp << ", id: " << a.id; } Book::Book() : bidSide(), askSide(), productType(TEST), product("a"), stationId("b"), unit("c"), expiry(std::chrono::nanoseconds(0)), aggFee(1), pasFee(-1), - broFee(2) {} + broFee(2) +{ +} Book::Book(ProductTypeEnum productType, std::string product, std::string stationId, std::string unit, @@ -97,65 +86,89 @@ double broFee) : bidSide{}, askSide{}, productType{productType}, product(product), stationId(stationId), unit(unit), expiry(expiry), aggFee(aggFee), - pasFee(pasFee), broFee(broFee) {} + pasFee(pasFee), broFee(broFee) +{ +} -void Book::ask(Order &order) { - while (this->bidSide.size() && this->bidSide.top().price >= order.price) { - if (this->bidSide.top().volume > order.remaining_volume) { - int temp = this->bidSide.top().volume; - order.filled_volume += temp; - this->bidSide.topRemoveVolume(order.remaining_volume); - order.remaining_volume -= temp; - break; - } else { - order.remaining_volume -= this->bidSide.top().volume; - order.filled_volume += this->bidSide.top().volume; - this->bidSide.pop(); +void Book::ask(Order& order) +{ + while (this->bidSide.size() && this->bidSide[0].price >= order.price) { + if (this->bidSide[0].volume > order.remaining_volume) { + int temp = this->bidSide[0].volume; + order.filled_volume += temp; + this->bidSide.front().volume -= order.remaining_volume; + order.remaining_volume -= temp; + break; + } else { + order.remaining_volume -= this->bidSide[0].volume; + order.filled_volume += this->bidSide[0].volume; + this->bidSide.erase(this->bidSide.begin()); + std::make_heap(this->bidSide.begin(), this->bidSide.end(), + std::less<Level>()); + } } - } - if (order.remaining_volume > 0) { - this->askSide.emplace(order); - } -} - -void Book::bid(Order &order) { - while (this->askSide.size() && this->askSide.top().price <= order.price) { - if (this->askSide.top().volume > order.remaining_volume) { - int temp = this->askSide.top().volume; - order.filled_volume += temp; - this->askSide.topRemoveVolume(order.remaining_volume); - order.remaining_volume -= temp; - break; - } else { - order.remaining_volume -= this->askSide.top().volume; - order.filled_volume += this->askSide.top().volume; - this->askSide.pop(); + if (order.remaining_volume > 0) { + this->askSide.emplace(this->askSide.begin(), order); + std::make_heap(this->askSide.begin(), this->askSide.end(), + std::greater<Level>()); } - } - if (order.remaining_volume > 0) { - this->bidSide.emplace(order); - } } -void Book::printBook(std::size_t numOrders) { - std::cout << "Sell side: " << this->askSide.size() << std::endl; - this->askSide.printTop(numOrders); - std::cout << "Buy side: " << this->bidSide.size() << std::endl; - this->bidSide.printTop(numOrders); +void Book::bid(Order& order) +{ + while (this->askSide.size() && this->askSide[0].price <= order.price) { + if (this->askSide[0].volume > order.remaining_volume) { + int temp = this->askSide.front().volume; + order.filled_volume += temp; + this->askSide.front().volume -= order.remaining_volume; + order.remaining_volume -= temp; + break; + } else { + order.remaining_volume -= this->askSide[0].volume; + order.filled_volume += this->askSide[0].volume; + this->askSide.erase(this->askSide.begin()); + std::make_heap(this->askSide.begin(), this->askSide.end(), + std::greater<Level>()); + } + } + if (order.remaining_volume > 0) { + this->bidSide.emplace(this->bidSide.begin(), order); + std::make_heap(this->bidSide.begin(), this->bidSide.end(), + std::less<Level>()); + } } -Book testBook(int orders, bool printBook) { - Book b = Book(); - std::chrono::nanoseconds time(1); - for (int i = 1; i < orders; i++) { - Order t(i, Buy, 10, time++, "a"); - b.bid(t); - } - for (int i = orders + 1; i < 2 * orders; i++) { - Order t(i, Sell, 10, time++, "b"); - b.ask(t); - } - if (printBook) - b.printBook(orders - 1); - return b; +void Book::printBook(std::size_t numOrders) +{ + std::cout << "Sell side: " << this->askSide.size() << std::endl; + std::vector<Level> askCopy(this->askSide); + int count = 0; + std::sort(askCopy.begin(), askCopy.end()); + for (auto i = askCopy.rbegin(); i != askCopy.rend() && count < numOrders; i++, count++) { + std::cout << *i << std::endl; + } + std::cout << "Buy side: " << this->bidSide.size() << std::endl; + std::vector<Level> bidCopy(this->bidSide); + count = 0; + std::sort(bidCopy.begin(), bidCopy.end()); + for (auto i = bidCopy.rbegin(); i != bidCopy.rend() && count < numOrders; i++, count++) { + std::cout << *i << std::endl; + } + } + +Book testBook(int orders, bool printBook) +{ + Book b = Book(); + double time(1); + for (int i = 1; i < orders; i++) { + Order t(i, Buy, 10, time++, "a"); + b.bid(t); + } + for (int i = orders + 1; i < 2 * orders; i++) { + Order t(i, Sell, 10, time++, "b"); + b.ask(t); + } + if (printBook) b.printBook(orders - 1); + return b; } +} // namespace book diff --git a/book.hpp b/book.hpp index 9e3a89c..2240290 100644 --- a/book.hpp +++ b/book.hpp @@ -1,74 +1,67 @@ #pragma once +#include <algorithm> #include <chrono> #include <cstddef> #include <iostream> #include <queue> #include <string> #include <vector> -#include <algorithm> +namespace book +{ enum OrderSideEnum { Buy, Sell }; enum ProductTypeEnum { TEST, FUTURE, SPREAD, CALL, PUT }; struct Order { - double price; - OrderSideEnum side; - int remaining_volume; - int filled_volume; - std::chrono::nanoseconds timestamp; - std::string id; - Order(double price, OrderSideEnum side, int volume, - std::chrono::nanoseconds timestamp, std::string id); + double price; + OrderSideEnum side; + int remaining_volume; + int filled_volume; + double timestamp; + std::string id; + Order(double price, OrderSideEnum side, int volume, double timestamp, + std::string id); }; struct Level { - double price; - int volume; - OrderSideEnum side; - std::chrono::nanoseconds timestamp; - std::string id; + double price; + int volume; + OrderSideEnum side; + double timestamp; + std::string id; - Level(Order &order); + Level(Order& order); }; -bool operator>(const Level &a, const Level &b); -bool operator<(const Level &a, const Level &b); -bool operator>=(const Level &a, const Level &b); -bool operator<=(const Level &a, const Level &b); -bool operator==(const Level &a, const Level &b); +bool operator>(const Level& a, const Level& b); +bool operator<(const Level& a, const Level& b); +bool operator>=(const Level& a, const Level& b); +bool operator<=(const Level& a, const Level& b); +bool operator==(const Level& a, const Level& b); std::ostream& operator<<(std::ostream& out, const Level& a); -template <class T> -struct Side : public std::priority_queue<Level, std::vector<Level>, T> { -public: - void deleteLevel(std::string orderId); - void topRemoveVolume(int volume); - void printTop(std::size_t num = 5); -}; - -using AskSide = Side<std::greater<Level>>; -using BidSide = Side<std::less<Level>>; - struct Book { - BidSide bidSide; - AskSide askSide; - ProductTypeEnum productType; - std::string product; - std::string stationId; - std::string unit; - std::chrono::nanoseconds expiry; - double aggFee; - double pasFee; - double broFee; + std::vector<Level> bidSide; + std::vector<Level> askSide; + ProductTypeEnum productType; + std::string product; + std::string stationId; + std::string unit; + std::chrono::nanoseconds expiry; + double aggFee; + double pasFee; + double broFee; - Book(); - Book(ProductTypeEnum productType, std::string product, std::string stationId, - std::string unit, std::chrono::nanoseconds expiry, double aggFee, - double pasFee, double broFee); - void ask(Order &order); - void bid(Order &order); + Book(); + Book(ProductTypeEnum productType, std::string product, + std::string stationId, std::string unit, + std::chrono::nanoseconds expiry, double aggFee, double pasFee, + double broFee); + void ask(Order& order); + void bid(Order& order); void printBook(std::size_t numOrders = 10); }; Book testBook(int orders = 10, bool printBook = true); +} // namespace book diff --git a/bot.cpp b/bot.cpp new file mode 100644 index 0000000..3e80bf5 --- /dev/null +++ b/bot.cpp @@ -0,0 +1,6 @@ +#include "strat.hpp" + +int main(void) +{ + +} diff --git a/click.cpp b/click.cpp new file mode 100644 index 0000000..4b43885 --- /dev/null +++ b/click.cpp @@ -0,0 +1,77 @@ +#include "book.hpp" +#include "json.hpp" +#include "protocol.hpp" + +#include <cstdlib> +#include <getopt.h> +#include <iostream> +#include <unordered_map> + +enum clickType { Buy, Sell, Flash }; + +static std::unordered_map<std::string, clickType> mapClick; + +void initialise() +{ + mapClick = {{"BUY", Buy}, {"SELL", Sell}, {"FLASH", Flash}, + {"b", Buy}, {"s", Sell}, {"f", Flash}, + {"B", Buy}, {"S", Sell}, {"F", Flash}}; +} + +void usage() +{ + std::cout << "DESCRIPTION" << std::endl + << "Click trader using same algs" << std::endl + << std::endl + << "USAGE" << std::endl + << "\t-a product" << std::endl + << "\t-t click type (buy, sell, flash)" << std::endl + << "\t-p price" << std::endl + << "\t-v volume" << std::endl + << "\t-i id" << std::endl + << "Always need product, need side, price and volume for " + "adding/flash, need id for deleting" + << std::endl; +} + +int main(int argc, char** argv) +{ + int c; + int index; + std::string product, id; + double price; + clickType click; + uint64_t volume; + initialise(); + if (argc == 1) { + usage(), exit(0); + } + while ((c = getopt(argc, argv, "a::t::p::v::i::")) != -1) { + switch (c) { + case 'a': + product = std::string(optarg); + break; + case 's': + click = mapClick[optarg]; + break; + case 'p': + price = std::stod(optarg); + break; + case 'v': + volume = std::stoll(optarg); + break; + case 'i': + id = std::string(optarg); + break; + case '?': + default: + usage(); + exit(0); + } + } + switch (click) { + case Buy: + case Sell: + case Flash:; + } +} diff --git a/cpp-httplib b/cpp-httplib new file mode 160000 index 0000000..11e02e9 --- /dev/null +++ b/cpp-httplib @@ -0,0 +1 @@ +Subproject commit 11e02e901cb9078f814903493359281ad4064ed5 diff --git a/date b/date new file mode 160000 index 0000000..655b249 --- /dev/null +++ b/date @@ -0,0 +1 @@ +Subproject commit 655b249b8f463f690c53a19d6b4110297699e3c5 diff --git a/json.cpp b/json.cpp new file mode 100644 index 0000000..a650550 --- /dev/null +++ b/json.cpp @@ -0,0 +1,493 @@ +#include "json.hpp" +#include "book.hpp" +#include "date/include/date/date.h" +#include "protocol.hpp" +#include <chrono> +#include <cstddef> +#include <cstdint> +#include <cstring> +#include <deque> +#include <iomanip> +#include <netdb.h> +#include <numeric> +#include <queue> +#include <sstream> +#include <string> +#include <unordered_map> +#include <utility> + +namespace json +{ +static std::unordered_map<std::string, MessageTypes> mapTypes; +static std::unordered_map<MessageTypes, book::ProductTypeEnum> mapAnnounce; +static std::unordered_map<std::string, book::OrderSideEnum> mapOrder; +static std::unordered_map<std::string, TradeTypeEnum> mapTrade; +static std::unordered_map<book::OrderSideEnum, std::string> mapOrderSide; + +void initialise() +{ + mapTypes = {{"FUTURE", FUTURE_TYPE}, + {"SPREAD", SPREAD_TYPE}, + {"CALL", CALL_TYPE}, + {"PUT", PUT_TYPE}, + {"SETTLEMENT", SETTLEMENT}, + {"ADDED", ADDED}, + {"DELETED", DELETED}, + {"TRADE", TRADE}, + {"BROKER_REQUEST", BROKER_REQUEST}, + {"BROKER_ACK", BROKER_ACK}, + {"BROKER_CONFIRM", BROKER_CONFIRM}}; + + mapAnnounce = {{FUTURE_TYPE, book::FUTURE}, + {SPREAD_TYPE, book::SPREAD}, + {CALL_TYPE, book::CALL}, + {PUT_TYPE, book::PUT}}; + + mapOrder = {{"BUY", book::Buy}, {"SELL", book::Sell}}; + + mapTrade = {{"BUY_AGGRESSOR", BUY_AGGRESSOR}, + {"SELL_AGGRESSOR", SELL_AGGRESSOR}}; + + mapOrderSide = {{book::Buy, "BUY"}, {book::Sell, "SELL"}}; +} + +AnnounceMessage* announce(std::string& str); +SettleMessage* settle(std::string& str); +AddedMessage* added(std::string& str); +DeletedMessage* deleted(std::string& str); +TradeMessage* trade(std::string& str); +BrokerRequest* brokerReq(std::string& str); +BrokerAck* brokerAck(std::string& str); +BrokerConfirm* brokerCon(std::string& str); + +std::queue<Message*> parseMany(std::string& str) +{ + std::queue<Message*> out; + std::size_t startIndex = 0, endIndex = 0; + while (true) { + startIndex = str.find("{", endIndex); + if (startIndex == std::string::npos) break; + endIndex = str.find("},", startIndex); + std::string substr = str.substr(startIndex, endIndex - startIndex + 1); + // std::cout << substr << std::endl; + Message* a = parseSingle(substr); + out.push(a); + } + return out; +} + +Message* parseSingle(std::string& str) +{ + if (mapTypes.empty()) { + initialise(); + } + std::size_t startIndex = str.find("\"type\": \"") + 9; + std::size_t endIndex = str.find("\"", startIndex + 1); + Message* out; + switch (mapTypes[str.substr(startIndex, endIndex - startIndex)]) { + case FUTURE_TYPE: + case SPREAD_TYPE: + case CALL_TYPE: + case PUT_TYPE: + out = announce(str); + break; + case SETTLEMENT: + out = settle(str); + break; + case ADDED: + out = added(str); + break; + case DELETED: + out = deleted(str); + break; + case TRADE: + out = trade(str); + break; + case BROKER_REQUEST: + out = brokerReq(str); + break; + case BROKER_ACK: + out = brokerAck(str); + break; + case BROKER_CONFIRM: + out = brokerCon(str); + break; + default: + out = new Message(NONE, ""); + break; + } + return out; +} + +inline std::pair<std::size_t, std::size_t> +find_arg(std::string str, std::string a, bool quotes, bool end = false) +{ + std::size_t out[2]; + if (quotes) { + out[0] = str.find("\"" + a + "\": \"") + 5 + a.size(); + if (end) { + out[1] = str.find("\"}", out[0] + 1); + } else { + out[1] = str.find("\",", out[0] + 1); + } + } else { + out[0] = str.find("\"" + a + "\": ") + 4 + a.size(); + if (end) { + out[1] = str.find("}", out[0] + 1); + } else { + out[1] = str.find(",", out[0] + 1); + } + } + return std::make_pair(out[0], out[1]); +} + +AnnounceMessage* announce(std::string& str) +{ + std::pair<std::size_t, std::size_t> type, product, stationId, stationName, + unit, expiry, aggFee, pasFee, broFee, sequence, timestamp; + type = find_arg(str, "type", true, false); + product = find_arg(str, "product", true, false); + stationId = find_arg(str, "stationId", false, false); + stationName = find_arg(str, "stationName", true, false); + unit = find_arg(str, "unit", true, false); + expiry = find_arg(str, "expiry", true, false); + aggFee = find_arg(str, "aggressiveFee", false, false); + pasFee = find_arg(str, "passiveFee", false, false); + broFee = find_arg(str, "brokerFee", false, false); + sequence = find_arg(str, "sequence", false, false); + timestamp = find_arg(str, "timestamp", false, true); + std::stringstream expiryStream( + str.substr(expiry.first, expiry.second - expiry.first)); + std::chrono::nanoseconds exp_time; + expiryStream >> + date::parse("%Y-%m-%f %H:%M%z", exp_time); // Parsing is broken + return new AnnounceMessage( + mapTypes[str.substr(type.first, type.second - type.first)], + str.substr(product.first, product.second - product.first), + str.substr(stationId.first, stationId.second - stationId.first), + str.substr(stationName.first, stationName.second - stationName.first), + str.substr(unit.first, unit.second - unit.first), exp_time, + std::stod(str.substr(aggFee.first, aggFee.second - aggFee.first)), + std::stod(str.substr(pasFee.first, pasFee.second - pasFee.first)), + std::stod(str.substr(broFee.first, broFee.second - broFee.first)), + std::stoll( + str.substr(sequence.first, sequence.second - sequence.first)), + std::stod( + str.substr(timestamp.first, timestamp.second - timestamp.first))); +} + +SettleMessage* settle(std::string& str) +{ + std::pair<std::size_t, std::size_t> type, product, stationName, expiry, + price, sequence, timestamp; + type = find_arg(str, "type", true, false); + product = find_arg(str, "product", true, false); + stationName = find_arg(str, "stationName", true, false); + expiry = find_arg(str, "expiry", true, false); + price = find_arg(str, "price", false, false); + sequence = find_arg(str, "sequence", false, false); + timestamp = find_arg(str, "timestamp", false, true); + std::stringstream expiryStream( + str.substr(expiry.first, expiry.second - expiry.first)); + std::chrono::nanoseconds exp_time; + expiryStream >> date::parse("%Y-%m-%d %H:%M%z", exp_time); + return new SettleMessage( + mapTypes[str.substr(type.first, type.second - type.first)], + str.substr(product.first, product.second - product.first), + str.substr(stationName.first, stationName.second - stationName.first), + exp_time, + std::stod(str.substr(price.first, price.second - price.first)), + std::stoll( + str.substr(sequence.first, sequence.second - sequence.first)), + std::stod( + str.substr(timestamp.first, timestamp.second - timestamp.first))); +} + +AddedMessage* added(std::string& str) +{ + std::pair<std::size_t, std::size_t> type, product, id, side, price, filled, + resting, sequence, timestamp; + type = find_arg(str, "type", true, false); + product = find_arg(str, "product", true, false); + sequence = find_arg(str, "sequence", false, false); + timestamp = find_arg(str, "timestamp", false, true); + id = find_arg(str, "id", true, false); + side = find_arg(str, "side", true, false); + price = find_arg(str, "price", false, false); + filled = find_arg(str, "filled", false, false); + resting = find_arg(str, "resting", false, false); + return new AddedMessage( + mapTypes[str.substr(type.first, type.second - type.first)], + str.substr(product.first, product.second - product.first), + str.substr(id.first, id.second - id.first), + mapOrder[str.substr(side.first, side.second - side.first)], + std::stod(str.substr(price.first, price.second - price.first)), + std::stoll(str.substr(filled.first, filled.second - filled.first)), + std::stoll(str.substr(resting.first, resting.second - resting.first)), + std::stoll( + str.substr(sequence.first, sequence.second - sequence.first)), + std::stod( + str.substr(timestamp.first, timestamp.second - timestamp.first))); +} + +DeletedMessage* deleted(std::string& str) +{ + std::pair<std::size_t, std::size_t> type, product, id, side, sequence, + timestamp; + type = find_arg(str, "type", true, false); + product = find_arg(str, "product", true, false); + sequence = find_arg(str, "sequence", false, false); + timestamp = find_arg(str, "timestamp", false, true); + id = find_arg(str, "id", true, false); + side = find_arg(str, "side", true, false); + return new DeletedMessage( + mapTypes[str.substr(type.first, type.second - type.first)], + str.substr(product.first, product.second - product.first), + str.substr(id.first, id.second - id.first), + mapOrder[str.substr(side.first, side.second - side.first)], + std::stoll( + str.substr(sequence.first, sequence.second - sequence.first)), + std::stod( + str.substr(timestamp.first, timestamp.second - timestamp.first))); +} + +TradeMessage* trade(std::string& str) +{ + std::pair<std::size_t, std::size_t> type, product, price, volume, buyer, + seller, tradeType, passiveOrder, passiveOrderRemaining, sequence, + timestamp; + type = find_arg(str, "type", true, false); + product = find_arg(str, "product", true, false); + sequence = find_arg(str, "sequence", false, false); + timestamp = find_arg(str, "timestamp", false, true); + price = find_arg(str, "price", false, false); + volume = find_arg(str, "volume", false, false); + buyer = find_arg(str, "buyer", true, false); + seller = find_arg(str, "seller", true, false); + tradeType = find_arg(str, "tradeType", true, false); + passiveOrder = find_arg(str, "passiveOrder", true, false); + passiveOrderRemaining = + find_arg(str, "passiveOrderRemaining", false, false); + + return new TradeMessage( + mapTypes[str.substr(type.first, type.second - type.first)], + str.substr(product.first, product.second - product.first), + std::stod(str.substr(price.first, price.second - price.first)), + std::stoll(str.substr(volume.first, volume.second - volume.first)), + str.substr(buyer.first, buyer.second - buyer.first), + str.substr(seller.first, seller.second - seller.first), + mapTrade[str.substr(tradeType.first, + tradeType.second - tradeType.first)], + str.substr(passiveOrder.first, + passiveOrder.second - passiveOrder.first), + std::stoll(str.substr(passiveOrderRemaining.first, + passiveOrderRemaining.second - + passiveOrderRemaining.first)), + std::stoll( + str.substr(sequence.first, sequence.second - sequence.first)), + std::stod( + str.substr(timestamp.first, timestamp.second - timestamp.first))); +} + +BrokerRequest* brokerReq(std::string& str) +{ + std::pair<std::size_t, std::size_t> type, product, price, side, volume, + counterparty; + type = find_arg(str, "type", true, false); + product = find_arg(str, "product", true, false); + price = find_arg(str, "price", false, false); + side = find_arg(str, "side", true, false); + volume = find_arg(str, "volume", false, false); + counterparty = find_arg(str, "counterparty", true, false); + return new BrokerRequest( + mapTypes[str.substr(type.first, type.second - type.first)], + str.substr(product.first, product.second - product.first), + std::stod(str.substr(price.first, price.second - price.first)), + mapOrder[str.substr(side.first, side.second - side.first)], + std::stoll(str.substr(volume.first, volume.second - volume.first)), + str.substr(counterparty.first, + counterparty.second - counterparty.first)); +} + +BrokerAck* brokerAck(std::string& str) +{ + std::pair<std::size_t, std::size_t> type, product, price, side, volume, + counterparty, id, brokerTradeStatus, owner; + type = find_arg(str, "type", true, false); + product = find_arg(str, "product", true, false); + price = find_arg(str, "price", false, false); + side = find_arg(str, "side", true, false); + volume = find_arg(str, "volume", false, false); + counterparty = find_arg(str, "counterparty", true, false); + id = find_arg(str, "id", true, false); + brokerTradeStatus = find_arg(str, "brokerTradeStatus", true, false); + owner = find_arg(str, "owner", true, false); + + return new BrokerAck( + mapTypes[str.substr(type.first, type.second - type.first)], + str.substr(product.first, product.second - product.first), + std::stod(str.substr(price.first, price.second - price.first)), + mapOrder[str.substr(side.first, side.second - side.first)], + std::stoll(str.substr(volume.first, volume.second - volume.first)), + str.substr(counterparty.first, + counterparty.second - counterparty.first), + str.substr(id.first, id.second - id.first), + str.substr(brokerTradeStatus.first, + brokerTradeStatus.second - brokerTradeStatus.first), + str.substr(owner.first, owner.second - owner.first)); +} +BrokerConfirm* brokerCon(std::string& str) +{ + std::pair<std::size_t, std::size_t> type, product, price, side, volume, + counterparty, id; + type = find_arg(str, "type", true, false); + product = find_arg(str, "product", true, false); + price = find_arg(str, "price", false, false); + side = find_arg(str, "side", true, false); + volume = find_arg(str, "volume", false, false); + counterparty = find_arg(str, "counterparty", true, false); + id = find_arg(str, "id", true, false); + + return new BrokerConfirm( + mapTypes[str.substr(type.first, type.second - type.first)], + str.substr(product.first, product.second - product.first), + std::stod(str.substr(price.first, price.second - price.first)), + mapOrder[str.substr(side.first, side.second - side.first)], + std::stoll(str.substr(volume.first, volume.second - volume.first)), + str.substr(counterparty.first, + counterparty.second - counterparty.first), + str.substr(id.first, id.second - id.first)); +} + +Message::Message() : type(NONE), product("error") {} + +Message::Message(MessageTypes types, std::string product) + : type(types), product(product) +{ +} + +FromExchange::FromExchange(MessageTypes type, std::string product, + uint64_t sequence, double timestamp) + : Message(type, product), sequence(sequence), timestamp(timestamp) +{ +} + +ToExchange::ToExchange(MessageTypes type, std::string product) + : Message(type, product){}; + +Broker::Broker(MessageTypes type, std::string product, double price, + book::OrderSideEnum side, uint64_t volume, + std::string counterparty) + : Message(type, product), price(price), side(side), volume(volume), + counterparty(counterparty) +{ +} + +AnnounceMessage::AnnounceMessage(MessageTypes type, std::string product, + std::string stationId, std::string stationName, + std::string unit, + std::chrono::nanoseconds expiry, double aggFee, + double pasFee, double broFee, + uint64_t sequence, double timestamp) + : FromExchange(type, product, sequence, timestamp), stationId(stationId), + stationName(stationName), unit(unit), expiry(expiry), aggFee(aggFee), + pasFee(pasFee), broFee(broFee) +{ +} + +SettleMessage::SettleMessage(MessageTypes type, std::string product, + std::string stationName, + std::chrono::nanoseconds expiry, double price, + uint64_t sequence, double timestamp) + : FromExchange(type, product, sequence, timestamp), + stationName(stationName), expiry(expiry), price(price) +{ +} + +AddMessage::AddMessage(MessageTypes type, std::string product, double price, + book::OrderSideEnum side, uint64_t volume) + : ToExchange(type, product), price(price), side(side), volume(volume) +{ +} + +std::string AddMessage::as_string() +{ + if (mapOrderSide.empty()) initialise(); + return "{\"type\": \"ADD\", \"product\": \"" + this->product + + "\", \"price\": " + std::to_string(this->price) + ", \"side\": \"" + + mapOrderSide[this->side] + + "\", \"volume\": " + std::to_string(this->volume) + "}"; +} + +AddedMessage::AddedMessage(MessageTypes type, std::string product, + std::string id, book::OrderSideEnum side, + double price, uint64_t filled, uint64_t resting, + uint64_t sequence, double timestamp) + : FromExchange(type, product, sequence, timestamp), id(id), side(side), + price(price), filled(filled), resting(resting) +{ +} + +DeleteMessage::DeleteMessage(MessageTypes type, std::string product, + std::string id) + : ToExchange(type, product), id(id) +{ +} + +std::string DeleteMessage::as_string() +{ + if (mapOrderSide.empty()) initialise(); + return "{\"type\": \"DELETE\", \"product\": \"" + this->product + + "\", \"id\": \"" + this->id + "\"}"; +} + +DeletedMessage::DeletedMessage(MessageTypes type, std::string product, + std::string id, book::OrderSideEnum side, + uint64_t sequence, double timestamp) + : FromExchange(type, product, sequence, timestamp), id(id), side(side) +{ +} + +RejectMessage::RejectMessage(MessageTypes type, std::string product, + std::string error, uint64_t sequence, + double timestamp) + : FromExchange(type, product, sequence, timestamp), error(error) +{ +} + +TradeMessage::TradeMessage(MessageTypes type, std::string product, double price, + uint64_t volume, std::string buyer, + std::string seller, TradeTypeEnum tradeType, + std::string passiveOrder, + uint64_t passiveOrderRemaining, uint64_t sequence, + double timestamp) + : FromExchange(type, product, sequence, timestamp), price(price), + volume(volume), buyer(buyer), seller(seller), tradeType(tradeType), + passiveOrder(passiveOrder), passiveOrderRemaining(passiveOrderRemaining) +{ +} + +BrokerRequest::BrokerRequest(MessageTypes type, std::string product, + double price, book::OrderSideEnum side, + uint64_t volume, std::string counterparty) + : Broker(type, product, price, side, volume, counterparty) +{ +} + +BrokerAck::BrokerAck(MessageTypes type, std::string product, double price, + book::OrderSideEnum side, uint64_t volume, + std::string counterparty, std::string id, + std::string brokerTradeStatus, std::string owner) + : Broker(type, product, price, side, volume, counterparty), id(id), + brokerTradeStatus(brokerTradeStatus), owner(owner) +{ +} + +BrokerConfirm::BrokerConfirm(MessageTypes type, std::string product, + double price, book::OrderSideEnum side, + uint64_t volume, std::string counterparty, + std::string id) + : Broker(type, product, price, side, volume, counterparty), id(id) +{ +} +} // namespace json diff --git a/json.hpp b/json.hpp new file mode 100644 index 0000000..5d1fc2b --- /dev/null +++ b/json.hpp @@ -0,0 +1,169 @@ +#pragma once + +#include "book.hpp" + +#include <chrono> +#include <cstdint> +#include <ostream> +#include <queue> +#include <unordered_map> + +namespace json +{ +enum MessageTypes { + FUTURE_TYPE, + SPREAD_TYPE, + CALL_TYPE, + PUT_TYPE, + SETTLEMENT, + ADD, + ADDED, + DELETE, + DELETED, + REJECT, + TRADE, + BROKER_REQUEST, + BROKER_ACK, + BROKER_CONFIRM, + NONE +}; + +enum TradeTypeEnum { BUY_AGGRESSOR, SELL_AGGRESSOR }; + +struct Message { + MessageTypes type; + std::string product; + Message(MessageTypes type, std::string product); + Message(); + virtual ~Message() = default; +}; + +struct FromExchange : public Message { + uint64_t sequence; + double timestamp; + FromExchange(MessageTypes type, std::string product, uint64_t sequence, + double timestamp); + virtual ~FromExchange() = default; +}; + +struct ToExchange : public Message { + ToExchange(MessageTypes type, std::string product); + virtual ~ToExchange() = default; +}; + +struct Broker : public Message { + double price; + book::OrderSideEnum side; + uint64_t volume; + std::string counterparty; + Broker(MessageTypes type, std::string product, double price, + book::OrderSideEnum side, uint64_t volume, std::string counterparty); + virtual ~Broker() = default; +}; + +struct AnnounceMessage : public FromExchange { + std::string stationId; + std::string stationName; + std::string unit; + std::chrono::nanoseconds expiry; + double aggFee; + double pasFee; + double broFee; + + AnnounceMessage(MessageTypes type, std::string product, + std::string stationId, std::string stationName, + std::string unit, std::chrono::nanoseconds expiry, + double aggFee, double pasFee, double broFee, + uint64_t sequence, double timestamp); +}; + +struct SettleMessage : public FromExchange { + std::string stationName; + std::chrono::nanoseconds expiry; + double price; + SettleMessage(MessageTypes type, std::string product, + std::string stationName, std::chrono::nanoseconds expiry, + double price, uint64_t sequence, double timestamp); +}; + +struct AddMessage : public ToExchange { + double price; + book::OrderSideEnum side; + uint64_t volume; + AddMessage(MessageTypes type, std::string product, double price, + book::OrderSideEnum side, uint64_t volume); + std::string as_string(); +}; + +struct AddedMessage : public FromExchange { + std::string id; + book::OrderSideEnum side; + double price; + uint64_t filled; + uint64_t resting; + AddedMessage(MessageTypes type, std::string product, std::string id, + book::OrderSideEnum side, double price, uint64_t filled, + uint64_t resting, uint64_t sequence, double timestamp); +}; + +struct DeleteMessage : public ToExchange { + std::string id; + DeleteMessage(MessageTypes type, std::string product, std::string id); + std::string as_string(); +}; + +struct DeletedMessage : public FromExchange { + std::string id; + book::OrderSideEnum side; + DeletedMessage(MessageTypes type, std::string product, std::string id, + book::OrderSideEnum side, uint64_t sequence, + double timestamp); +}; + +struct RejectMessage : public FromExchange { + std::string error; + RejectMessage(MessageTypes type, std::string product, std::string error, + uint64_t sequence, double timestamp); +}; + +struct TradeMessage : public FromExchange { + double price; + uint64_t volume; + std::string buyer; + std::string seller; + TradeTypeEnum tradeType; + std::string passiveOrder; + uint64_t passiveOrderRemaining; + TradeMessage(MessageTypes type, std::string product, double price, + uint64_t volume, std::string buyer, std::string seller, + TradeTypeEnum tradeType, std::string passiveOrder, + uint64_t passiveOrderRemaining, uint64_t sequence, + double timestamp); +}; + +struct BrokerRequest : public Broker { + BrokerRequest(MessageTypes type, std::string product, double price, + book::OrderSideEnum side, uint64_t volume, + std::string counterparty); +}; + +struct BrokerAck : public Broker { + std::string id; + std::string brokerTradeStatus; + std::string owner; + BrokerAck(MessageTypes type, std::string product, double price, + book::OrderSideEnum side, uint64_t volume, + std::string counterparty, std::string id, + std::string brokerTradeStatus, std::string owner); +}; + +struct BrokerConfirm : public Broker { + std::string id; + BrokerConfirm(MessageTypes type, std::string product, double price, + book::OrderSideEnum side, uint64_t volume, + std::string counterparty, std::string id); +}; + +Message* parseSingle(std::string& str); +std::queue<Message*> parseMany(std::string& str); +} // namespace json diff --git a/main.cpp b/main.cpp index 612f52f..20d1aa9 100644 --- a/main.cpp +++ b/main.cpp @@ -1,7 +1,15 @@ #include "book.hpp" +#include "json.hpp" +#include "protocol.hpp" #include <chrono> int main(void) { - Book b = testBook(10, true); + book::Book b = book::testBook(10, true); + auto bs = protocol::recoverBook(); + std::cout << bs.size() << std::endl; + for (auto i : bs) { + std::cout << i.first << std::endl; + i.second.printBook(); + } } diff --git a/protocol.cpp b/protocol.cpp new file mode 100644 index 0000000..fbcc617 --- /dev/null +++ b/protocol.cpp @@ -0,0 +1,177 @@ +#include "protocol.hpp" + +#include "book.hpp" +#include "cpp-httplib/httplib.h" +#include "json.hpp" +#include "secrets.hpp" + +#include <chrono> +#include <iostream> +#include <queue> +#include <sstream> +#include <string> +#include <unordered_map> + +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); + +void initialise() +{ + 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; +} + +void addOrder(json::AddMessage& order) +{ + std::string message = "{\"message\": " + order.as_string() + ", " + + "\"username\": \"" + std::string(USER) + + "\", \"password\": \"" + std::string(PASS) + "\"}"; + send(message); +} + +void deleteOrder(json::DeleteMessage& order) +{ + std::string message = "{\"message\": " + order.as_string() + + ", \"username\": \"" + std::string(USER) + + "\", \"password\": \"" + std::string(PASS) + "\"}"; + 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:; + } +} + +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); +} +void settle(std::unordered_map<std::string, book::Book>& bs, + json::SettleMessage* message) +{ + 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); + } +} +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>()); + } + } + } 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); + } +} +void broker(std::unordered_map<std::string, book::Book>& bs, + json::Broker* message) +{ +} + +void send(std::string& message) +{ + cli.Post("/execution", message, "text/plain"); +} + +} // namespace protocol diff --git a/protocol.hpp b/protocol.hpp new file mode 100644 index 0000000..94f6476 --- /dev/null +++ b/protocol.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "book.hpp" +#include "json.hpp" + +#include <string> +#include <unordered_map> + +namespace protocol +{ +enum OrderEnum { ADD, DELETE, BROKER_REQUEST, BROKER_CONFIRM }; + + // Catchup utilities +std::unordered_map<std::string, book::Book> recoverBook(); + + // Outgoing messages + void addOrder(json::AddMessage& order); + void deleteOrder(json::DeleteMessage& order); + +// Incoming messages +void handleMessage(std::unordered_map<std::string, book::Book>& bs, + json::Message* message); +void announce(std::unordered_map<std::string, book::Book>& bs, + json::AnnounceMessage* message); +void settle(std::unordered_map<std::string, book::Book>& bs, + json::SettleMessage* message); +void addedOrder(std::unordered_map<std::string, book::Book>& bs, + json::AddedMessage* message); +void deletedOrder(std::unordered_map<std::string, book::Book>& bs, + json::DeletedMessage* message); +void tradeOrder(std::unordered_map<std::string, book::Book>& bs, + json::TradeMessage* message); +void broker(std::unordered_map<std::string, book::Book>& bs, + json::Broker* message); + + void send(std::string& message); +} // namespace protocol diff --git a/secrets.hpp b/secrets.hpp index b0622e5..d9d7ae8 100644 --- a/secrets.hpp +++ b/secrets.hpp @@ -1,11 +1,15 @@ #pragma once -#define HOST "sytev070" +#include <string> + +#define TEST + +constexpr const char* HOST="sytev070"; #ifdef TEST -#define PORT "9005" +constexpr const char* PORT="9005"; #else -#define PORT "9000" +constexpr const char* PORT="9000"; #endif -#define USER "jgrunbau" -#define PASS "b7d630945a0854581d9f86ba147f34a5" +constexpr const char* USER="jgrunbau"; +constexpr const char* PASS="b7d630945a0854581d9f86ba147f34a5"; diff --git a/strat.cpp b/strat.cpp new file mode 100644 index 0000000..ef8b0ba --- /dev/null +++ b/strat.cpp @@ -0,0 +1,5 @@ +#include "strat.hpp" + +namespace strat { + +} diff --git a/strat.hpp b/strat.hpp index 212f323..893edce 100644 --- a/strat.hpp +++ b/strat.hpp @@ -1,9 +1,8 @@ #pragma once #include "book.hpp" -#include "secrets.hpp" +#include <unordered_map> +#include <string> -Book recoverBook() -{ - +namespace strat { } diff --git a/test.cpp b/test.cpp new file mode 100644 index 0000000..20d1aa9 --- /dev/null +++ b/test.cpp @@ -0,0 +1,15 @@ +#include "book.hpp" +#include "json.hpp" +#include "protocol.hpp" +#include <chrono> + +int main(void) +{ + book::Book b = book::testBook(10, true); + auto bs = protocol::recoverBook(); + std::cout << bs.size() << std::endl; + for (auto i : bs) { + std::cout << i.first << std::endl; + i.second.printBook(); + } +} -- Gitblit v1.9.3