Joel Grunbaum
2022-01-19 0b7aa02704f6ece97d17fbb118519c5cc62caaba
Bot prints exchange feed
7 files modified
333 ■■■■ changed files
bot.cpp 22 ●●●●● patch | view | raw | blame | history
json.cpp 118 ●●●● patch | view | raw | blame | history
json.hpp 9 ●●●● patch | view | raw | blame | history
protocol.cpp 57 ●●●●● patch | view | raw | blame | history
protocol.hpp 5 ●●●● patch | view | raw | blame | history
strat.cpp 115 ●●●●● patch | view | raw | blame | history
strat.hpp 7 ●●●●● patch | view | raw | blame | history
bot.cpp
@@ -1,3 +1,23 @@
#include "strat.hpp"
#include <csignal>
#include <cstdlib>
#include <iostream>
int main(void) {}
void signalHandler(int signum)
{
    strat::destroy();
    std::exit(0);
}
void no(int signum)
{
    std::cout << "no." << std::endl;
}
int main(void)
{
    signal(SIGINT, signalHandler);
    // signal(SIGTERM, no);
    strat::initialise();
    strat::mainLoop();
}
json.cpp
@@ -24,6 +24,8 @@
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;
static std::unordered_map<MessageTypes, std::string> mapAStr;
static std::unordered_map<TradeTypeEnum, std::string> mapTTStr;
void initialise()
{
@@ -51,6 +53,15 @@
                {"BROKER_TRADE", BROKER_TRADE}};
    mapOrderSide = {{book::Buy, "BUY"}, {book::Sell, "SELL"}};
    mapAStr = {{FUTURE_TYPE, "FUTURE"},
               {SPREAD_TYPE, "SPREAD"},
               {CALL_TYPE, "CALL"},
               {PUT_TYPE, "PUT"}};
    mapTTStr = {{BUY_AGGRESSOR, "BUY_AGGRESSOR"},
                {SELL_AGGRESSOR, "SELL_AGGRESSOR"},
                {BROKER_TRADE, "BROKER_TRADE"}};
}
Message* parseSingle(rapidjson::Value& d);
@@ -153,8 +164,8 @@
SettleMessage* settle(rapidjson::Value& d)
{
    std::chrono::nanoseconds exp_time(0);
    std::string es = d["expiry"].GetString();
    exp_time = parseTime(es);
    std::string es = d["expiry"].GetString();
    exp_time = parseTime(es);
    return new SettleMessage(
        mapTypes[d["type"].GetString()], d["product"].GetString(),
        d["stationName"].GetString(), exp_time, d["price"].GetDouble(),
@@ -163,11 +174,12 @@
AddedMessage* added(rapidjson::Value& d)
{
    return new AddedMessage(
        mapTypes[d["type"].GetString()], d["product"].GetString(),
        d["id"].GetString(), mapOrder[d["side"].GetString()],
        d["price"].GetDouble(), d["filled"].GetInt(), d["resting"].GetInt(),
        d["sequence"].GetInt(), d["timestamp"].GetDouble());
    return new AddedMessage(mapTypes[d["type"].GetString()],
                            d["product"].GetString(), d["id"].GetString(),
                            mapOrder[d["side"].GetString()],
                            d["price"].GetDouble(), d["filled"].GetInt(),
                            d["resting"].GetInt(), d["owner"].GetString(),
                            d["sequence"].GetInt(), d["timestamp"].GetDouble());
}
DeletedMessage* deleted(rapidjson::Value& d)
@@ -270,6 +282,20 @@
{
}
std::string AnnounceMessage::as_string()
{
    return "{\"type\": \"" + mapAStr[this->type] =
               "\", \"product\": \"" + this->product +
               "\", \"stationId\": " + std::to_string(this->stationId) +
               "\", \"stationName\": \"" + this->stationName +
               "\", \"unit\": \"" + this->unit +
               "\", \"expiry\": " + std::to_string(this->expiry.count()) +
               ", \"aggressiveFee\": " + std::to_string(this->aggFee) +
               ", \"passiveFee\": " + std::to_string(this->pasFee) +
               ", \"sequence\": " + std::to_string(this->sequence) +
               ", \"timestamp\": " + std::to_string(this->timestamp) + "}";
}
SettleMessage::SettleMessage(MessageTypes type, std::string product,
                             std::string stationName,
                             std::chrono::nanoseconds expiry, double price,
@@ -277,6 +303,17 @@
    : FromExchange(type, product, sequence, timestamp),
      stationName(stationName), expiry(expiry), price(price)
{
}
std::string SettleMessage::as_string()
{
    return "{\"type\": \"" + mapAStr[this->type] =
               "\", \"product\": \"" + this->product +
               "\", \"stationName\": \"" + this->stationName +
               "\", \"expiry\": " + std::to_string(this->expiry.count()) +
               ", \"price\": " + std::to_string(this->price) +
               ", \"sequence\": " + std::to_string(this->sequence) +
               ", \"timestamp\": " + std::to_string(this->timestamp) + "}";
}
AddMessage::AddMessage(MessageTypes type, std::string product, double price,
@@ -297,22 +334,24 @@
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)
                           std::string owner, uint64_t sequence,
                           double timestamp)
    : FromExchange(type, product, sequence, timestamp), id(id), side(side),
      price(price), filled(filled), resting(resting)
      price(price), filled(filled), resting(resting), owner(owner)
{
}
std::string AddedMessage::as_string()
{
    return "{\"type\": \"ADDED\", \"product\": \"" + this->product +
           "\", \"product\": \"" + this->id + "\" \"side\": \"" +
           "\", \"product\": \"" + this->id + "\", \"side\": \"" +
           mapOrderSide[this->side] +
           "\", \"price\": " + std::to_string(this->price) +
           "\"filled\": " + std::to_string(this->filled) +
           ", \"filled\": " + std::to_string(this->filled) +
           ", \"resting\": " + std::to_string(this->resting) +
           ", \"sequence\": " + std::to_string(this->sequence) +
           ", \"timestamp\":" + std::to_string(this->timestamp) + "}";
           ", \"owner\": \"" + this->owner +
           "\", \"sequence\": " + std::to_string(this->sequence) +
           ", \"timestamp\": " + std::to_string(this->timestamp) + "}";
}
DeleteMessage::DeleteMessage(MessageTypes type, std::string product,
@@ -338,10 +377,10 @@
std::string DeletedMessage::as_string()
{
    return "{\"type\": \"DELETED\", \"product\": \"" + this->product +
           "\", \"product\": \"" + this->id + "\" \"side\": \"" +
           "\", \"product\": \"" + this->id + "\", \"side\": \"" +
           mapOrderSide[this->side] +
           ", \"sequence\": " + std::to_string(this->sequence) +
           ", \"timestamp\":" + std::to_string(this->timestamp) + "}";
           ", \"timestamp\": " + std::to_string(this->timestamp) + "}";
}
RejectMessage::RejectMessage(MessageTypes type, std::string product,
@@ -371,11 +410,35 @@
{
}
std::string TradeMessage::as_string()
{
    return "{\"type\": \"TRADE\", \"product\": \"" + this->product +
           "\", \"price\": " + std::to_string(this->price) +
           ", \"volume\": " + std::to_string(this->volume) + ", \"buyer\": \"" +
           this->buyer + "\", \"seller\": \"" + this->seller +
           "\", \"tradeType\": \"" + mapTTStr[this->tradeType] +
           "\", \"passiveOrder\": \"" + this->passiveOrder +
           "\", \"passoveOrderRemaining\": " +
           std::to_string(this->passiveOrderRemaining) +
           "\", \"sequence\": " + std::to_string(this->sequence) +
           ", \"timestamp\": " + std::to_string(this->timestamp) + "}";
}
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)
{
}
std::string BrokerRequest::as_string()
{
    return "{\"type\": \"" + mapAStr[this->type] =
               "\", \"product\": \"" + this->product +
               "\", \"price\": " + std::to_string(this->price) +
               ", \"side\": " + mapOrderSide[this->side] +
               ", \"volume\": " + std::to_string(this->volume) +
               ", \"counterparty\": \"" + this->counterparty + "\"}";
}
BrokerAck::BrokerAck(MessageTypes type, std::string product, double price,
@@ -387,6 +450,19 @@
{
}
std::string BrokerAck::as_string()
{
    return "{\"type\": \"" + mapAStr[this->type] =
               "\", \"product\": \"" + this->product +
               "\", \"price\": " + std::to_string(this->price) +
               ", \"side\": " + mapOrderSide[this->side] +
               ", \"volume\": " + std::to_string(this->volume) +
               ", \"counterparty\": \"" + this->counterparty + +"\", \"id\"" +
               this->id +
               "\", \"brokerTradeStatus\": " + this->brokerTradeStatus +
               "\", \"owner\": \"" + this->owner + "\"}";
}
BrokerConfirm::BrokerConfirm(MessageTypes type, std::string product,
                             double price, book::OrderSideEnum side,
                             uint64_t volume, std::string counterparty,
@@ -394,4 +470,16 @@
    : Broker(type, product, price, side, volume, counterparty), id(id)
{
}
std::string BrokerConfirm::as_string()
{
    return "{\"type\": \"" + mapAStr[this->type] =
               "\", \"product\": \"" + this->product +
               "\", \"price\": " + std::to_string(this->price) +
               ", \"side\": " + mapOrderSide[this->side] +
               ", \"volume\": " + std::to_string(this->volume) +
               ", \"counterparty\": \"" + this->counterparty +
               "\", \"id\": " + this->id + "\"}";
}
} // namespace json
json.hpp
@@ -82,6 +82,7 @@
                    std::chrono::nanoseconds expiry, double aggFee,
                    double pasFee, double broFee, uint64_t sequence,
                    double timestamp);
    std::string as_string();
};
struct SettleMessage : public FromExchange {
@@ -91,6 +92,7 @@
    SettleMessage(MessageTypes type, std::string product,
                  std::string stationName, std::chrono::nanoseconds expiry,
                  double price, uint64_t sequence, double timestamp);
    std::string as_string();
};
struct AddMessage : public ToExchange {
@@ -108,9 +110,10 @@
    double price;
    uint64_t filled;
    uint64_t resting;
    std::string owner;
    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);
                 uint64_t resting, std::string owner, uint64_t sequence, double timestamp);
    std::string as_string();
};
@@ -149,12 +152,14 @@
                 TradeTypeEnum tradeType, std::string passiveOrder,
                 uint64_t passiveOrderRemaining, uint64_t sequence,
                 double timestamp);
    std::string as_string();
};
struct BrokerRequest : public Broker {
    BrokerRequest(MessageTypes type, std::string product, double price,
                  book::OrderSideEnum side, uint64_t volume,
                  std::string counterparty);
    std::string as_string();
};
struct BrokerAck : public Broker {
@@ -165,6 +170,7 @@
              book::OrderSideEnum side, uint64_t volume,
              std::string counterparty, std::string id,
              std::string brokerTradeStatus, std::string owner);
    std::string as_string();
};
struct BrokerConfirm : public Broker {
@@ -172,6 +178,7 @@
    BrokerConfirm(MessageTypes type, std::string product, double price,
                  book::OrderSideEnum side, uint64_t volume,
                  std::string counterparty, std::string id);
    std::string as_string();
};
std::queue<Message*> parse(std::string& str);
protocol.cpp
@@ -22,6 +22,7 @@
    mapAnnounce;
std::string server = std::string(HOST) + ":" + std::string(PORT);
httplib::Client cli("http://" + server);
std::unique_ptr<easywsclient::WebSocket> ws;
double lastime = 0;
@@ -57,32 +58,44 @@
    return bs;
}
void catchUp(std::unordered_map<std::string, book::Book>& bs)
void createWebSocket()
{
    static std::unique_ptr<easywsclient::WebSocket> ws(
        easywsclient::WebSocket::from_url("ws://" + server + "/information"));
    std::string feed;
    bool gotMessage = false;
    ws = std::unique_ptr<easywsclient::WebSocket>(
        easywsclient::WebSocket::pointer(easywsclient::WebSocket::from_url(
            "ws://" + server + "/information")));
    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;
}
std::deque<json::Message*>
catchUp(std::unordered_map<std::string, book::Book>& bs)
{
    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();
            }
            protocol::handleMessage(bs, a.front());
            delete a.front();
            a.pop();
        }
    }
    } while (gotMessage);
    return out;
}
json::Message* addOrder(json::AddMessage& order)
protocol.hpp
@@ -3,6 +3,7 @@
#include "book.hpp"
#include "json.hpp"
#include <deque>
#include <string>
#include <unordered_map>
@@ -12,7 +13,9 @@
// Catchup utilities
std::unordered_map<std::string, book::Book> recoverBook();
void catchUp(std::unordered_map<std::string, book::Book>& bs);
void createWebSocket();
std::deque<json::Message*>
catchUp(std::unordered_map<std::string, book::Book>& bs);
// Outgoing messages
json::Message* addOrder(json::AddMessage& order);
strat.cpp
@@ -1,6 +1,121 @@
#include "strat.hpp"
#include "bom.hpp"
#include "book.hpp"
#include "json.hpp"
#include "secrets.hpp"
#include "protocol.hpp"
#include <deque>
#include <unordered_map>
namespace strat
{
std::unordered_map<std::string, json::Message*> orders;
void deleteOrder(json::AddedMessage* m);
void freeMessages(std::deque<json::Message*>& m);
void printFeed(std::deque<json::Message*>& m);
void dumbHit(std::deque<json::Message*>& m);
void initialise()
{
    bom::initialise();
    protocol::createWebSocket();
}
void destroy()
{
    bom::destroy();
    for (auto& i : orders) {
        switch (i.second->type) {
        case json::FUTURE_TYPE:
        case json::SPREAD_TYPE:
        case json::CALL_TYPE:
        case json::PUT_TYPE:
            deleteOrder((json::AddedMessage*)i.second);
        default:;
        }
        delete i.second;
    }
}
void mainLoop()
{
    auto bs = protocol::recoverBook();
    while (true) {
        auto messages = protocol::catchUp(bs);
        // bom::updateBom(bs);
        printFeed(messages);
        freeMessages(messages);
    }
}
void deleteOrder(json::AddedMessage* m)
{
    json::DeleteMessage a(json::DELETE, m->product, m->id);
    delete protocol::deleteOrder(a);
}
void printFeed(std::deque<json::Message*>& m)
{
    for (auto& i : m) {
        switch (i->type) {
        case json::FUTURE_TYPE:
        case json::SPREAD_TYPE:
        case json::CALL_TYPE:
        case json::PUT_TYPE:
            std::cout << ((json::AnnounceMessage*)i)->as_string() << std::endl;
            break;
        case json::SETTLEMENT:
            std::cout << ((json::SettleMessage*)i)->as_string() << std::endl;
            break;
        case json::ADDED:
            std::cout << ((json::AddedMessage*)i)->as_string() << std::endl;
            break;
        case json::DELETED:
            std::cout << ((json::DeletedMessage*)i)->as_string() << std::endl;
            break;
        case json::TRADE:
            std::cout << ((json::TradeMessage*)i)->as_string() << std::endl;
            break;
        case json::BROKER_REQUEST:
            std::cout << ((json::BrokerRequest*)i)->as_string() << std::endl;
            break;
        case json::BROKER_ACK:
            std::cout << ((json::BrokerAck*)i)->as_string() << std::endl;
            break;
        case json::BROKER_CONFIRM:
            std::cout << ((json::BrokerConfirm*)i)->as_string() << std::endl;
            break;
        default:;
        }
    }
}
void freeMessages(std::deque<json::Message*>& m)
{
    for (auto i : m) {
        delete i;
    }
}
void dumbHit(std::deque<json::Message*>& m)
{
    for (auto& i : m) {
        if (i->type == json::ADDED) {
            json::AddedMessage* j = (json::AddedMessage*) i;
            if (j->owner == "nseow") {
                book::OrderSideEnum s = j->side == book::Buy ? book::Sell : book::Buy;
                json::AddMessage a(json::ADD, j->product, j->price, s, j->resting);
                auto b = protocol::addOrder(a);
                if (b->type == json::ADDED) {
                    json::DeleteMessage d(json::DELETE, j->product, ((json::AddedMessage*) b)->id);
                    delete protocol::deleteOrder(d);
                }
                delete b;
            }
        }
    }
}
} // namespace strat
strat.hpp
@@ -1,9 +1,8 @@
#pragma once
#include "book.hpp"
#include <string>
#include <unordered_map>
namespace strat
{
    void initialise();
    void destroy();
    void mainLoop();
}