← source index

src/OrderBook.cpp

240 lines  ·  6.9 K  ·  cpp
#include "OrderBook.h"

OrderBook::OrderBook(const std::string& symbol)
    : mSymbol(symbol)
    , mInitBookWidth(0)
{
#ifndef MAP_BASED_ORDERBOOK
    mBidLevels.reserve(10000);
    mAskLevels.reserve(10000);
#endif
}

std::string OrderBook::toStr(int depth, int spacing)
{
    std::string book = "";
    int widthBid = 0;
    int widthAsk = 0;

    try
    {
        int currentDepth = 0;
        std::string bids = "";
        std::string asks = "";
        std::vector<std::string> bidsBuf;
        std::vector<std::string> asksBuf;

        for(const auto& it: mBidLevels)
        {
            if(depth > 0) currentDepth++;
            if(currentDepth > depth) break;

            std::stringstream ss;
            ss << std::fixed << std::setprecision(5) << it.first;

            bool isFirstLine = false;
            if(bids.size() == 0) isFirstLine = true;
            bids += ss.str() + ' ' + std::to_string(it.second) + "|\n";
            if(isFirstLine) widthBid = bids.size();
        }

        currentDepth = 0;
        for(const auto& it: mAskLevels)
        {
            if(depth > 0) currentDepth++;
            if(currentDepth > depth) break;

            std::stringstream ss;
            ss << std::fixed << std::setprecision(5) << it.first;

            bool isFirstLine = false;
            if(asks.size() == 0) isFirstLine = true;
            asks += ss.str() + ' ' + std::to_string(it.second) + "\n";
            if(isFirstLine) widthAsk = asks.size();
        }

        if(bids.size() == 0 && asks.size() == 0) return "";

        boost::split(bidsBuf, bids, boost::is_any_of("\n"));
        boost::split(asksBuf, asks, boost::is_any_of("\n"));

        int maxSize = std::max(bidsBuf.size(), asksBuf.size());
        if(depth > 0) maxSize = std::min(depth, maxSize);
        bidsBuf.resize(maxSize, "");
        asksBuf.resize(maxSize, "");
        std::string bookSpacing(spacing, ' ');

        for(unsigned int i = 0; i < maxSize; i++)
        {
            if(bidsBuf[i].size() == 0)
            {
                bidsBuf[i].resize(asksBuf[i].size() + 1, ' ');
                bidsBuf[i][asksBuf[i].size()] = '|';
            }
            else if(asksBuf[i].size() == 0)
            {
                asksBuf[i].resize(bidsBuf[i].size() + 1, ' ');
                asksBuf[i] = '|' + asksBuf[i];
            }

            if(book == "")
            {
                try
                {
                    //throws an exception I could not fix just yet... quite infuriating
                    //last value of halfLineSize right befor the throw is 04
                    int halfLineSize = std::max(bidsBuf[i].size(), asksBuf[i].size());
                    std::string bidLine = "BID";
                    bidLine = std::string((halfLineSize - bidLine.size()) * 0.5, ' ') + "BID" + std::string((halfLineSize - bidLine.size() - mSymbol.size()) * 0.5, ' ');
                    std::string askLine = "ASK";
                    askLine = std::string((halfLineSize - askLine.size() - mSymbol.size()) * 0.5, ' ') + "ASK" + std::string((halfLineSize - askLine.size()) * 0.5, ' ');
                    std::string bookTitle = bidLine + mSymbol + askLine + "\n";
                    book += bookTitle;
                    if(!mInitBookWidth) mInitBookWidth = bookTitle.size();
                }
                catch(const std::exception& e)
                {}   
            }

            std::string line = bidsBuf[i] + asksBuf[i] + "\n";
            book += bookSpacing + line;
        }
    }
    catch(const std::exception& e)
    {}
    
    return book;
}

#ifdef ITERATOR_BASED_ORDERBOOK
void OrderBook::addOrder(EntrySide side, double price, double volume)
{
    std::map<double, double>::iterator it;
    Entry lookupEntry;
    lookupEntry.side = side;

    if(side == EntrySide::BID)
    {
        it = add(mBidLevels, price, volume);
        lookupEntry.bidIt = it;
    }
    else if(side == EntrySide::ASK)
    {
        it = add(mAskLevels, price, volume);
        lookupEntry.askIt = it;
    }
    std::greater<int> aa;
    
    
    mLookup.emplace(price, lookupEntry);
}

void OrderBook::removeOrder(EntrySide side, double price)
{
    auto it = mLookup.find(price);

    if(it != mLookup.end())
    {
        if(side == EntrySide::BID)
            remove(it->second.bidIt, mBidLevels);
        else if(side == EntrySide::ASK)
            remove(it->second.askIt, mAskLevels);
    }

    mLookup.erase(it);
}

template <class T>
typename T::iterator OrderBook::add(T& levels, double price, double volume)
{
    auto [it, status] = levels.try_emplace(price, volume);
    if(!status) it->second = volume;
    return it;
}

template<class T>
void OrderBook::remove(typename T::iterator it, T& levels)
{
    levels.erase(it);
}
#else

void OrderBook::addOrder(EntrySide side, double price, double volume)
{
#ifdef MAP_BASED_ORDERBOOK
    if(side == EntrySide::BID)
        add(mBidLevels, price, volume);
    else if(side == EntrySide::ASK)
        add(mAskLevels, price, volume);
#else
    if(side == EntrySide::BID)
        add(mBidLevels, price, volume, std::greater<double>());
    else if(side == EntrySide::ASK)
        add(mAskLevels, price, volume, std::less<double>());
#endif
}

void OrderBook::removeOrder(EntrySide side, double price)
{
    //Unfortunately, for now, this will do O(log n) search time
#ifdef MAP_BASED_ORDERBOOK
    if(side == EntrySide::BID)
        remove(mBidLevels, price);
    else if(side == EntrySide::ASK)
        remove(mAskLevels, price);
#else
    if(side == EntrySide::BID)
        remove(mBidLevels, price, std::greater<double>());
    else if(side == EntrySide::ASK)
        remove(mAskLevels, price, std::less<double>());
#endif
}

#ifdef MAP_BASED_ORDERBOOK
template <class T>
void OrderBook::add(T& levels, double price, double volume)
{
    auto [it, status] = levels.try_emplace(price, volume);
    if(!status) it->second = volume;
}

template<class T>
void OrderBook::remove(T& levels, double price)
{
    levels.erase(price);
}
#else
template <class T, class C>
void OrderBook::add(T& levels, double price, double volume, C comp)
{
    auto it = std::lower_bound(levels.begin(), levels.end(), price, 
        [comp] (const auto& p, double _price) { return comp(p.first, _price); });
    if(it != levels.end() && it->first == price) it->second = volume;
        else levels.insert(it, {price, volume});

#ifdef ASSERT_ON_TOP_OF_BOOK_CROSSING
    if(!(mAskLevels.empty() || mBidLevels.empty())) assert(mAskLevels.front().first > mBidLevels.front().first);
#endif
}

template<class T, class C>
void OrderBook::remove(T& levels, double price, C comp)
{
    auto it = std::lower_bound(levels.begin(), levels.end(), price, 
        [comp] (const auto& p, double _price) { return comp(p.first, _price); });
    if(it != levels.end() && it->first == price) levels.erase(it);

}
#endif
#endif

void OrderBook::clear()
{
#ifdef ITERATOR_BASED_ORDERBOOK
    mLookup.clear();
#endif

    mInitBookWidth = 0;
    mBidLevels.clear();
    mAskLevels.clear();
}