繁体   English   中英

构建比特币交易所的Orderbook表示

[英]Building an Orderbook representation for a Bitcoin exchange

我正在尝试为Poloniex比特币交易所建立一个Orderbook表示。 我订阅了Push-API ,它通过Websocket发送Orderbook的更新。 问题是我的订单会随着时间的推移变得不一致 ,即应该删除的订单仍在我的书中。

以下图片中的订单簿具有以下格式:

Exchange-Name - ASK - Amount - Price | Price - Amount - BID - Exchange-Name

在此输入图像描述

在左侧( ASK )是出售货币的人。 在右侧( BID )是购买货币的人。 BTCUSDETHBTCETHUSD描述了不同的市场。 BTCUSD意味着比特币兑换美元, ETHBTC意味着以太币交换比特币, ETHUSD意味着以太币交换美元。

Poloniex以JSON格式通过Websocket发送更新。 以下是此类更新的示例:

[
   36,
   7597659581972377,
   8089731807973507,
   {},
   [
      {"data":{"rate":"609.00000029","type":"bid"},"type":"orderBookRemove"},{"data":{"amount":"0.09514285","rate":"609.00000031","type":"bid"},"type":"orderBookModify"}
   ],
   {
      "seq":19976127
   }
]
  • 这个问题可以忽略json [0]
  • json [1]是市场标识符。 这意味着我发送了一个请求,如“ 订阅市场BTCUSD ”,他们回答“ BTCUSD更新将在标识号7597659581972377下发送 ”。

  • 这个问题可以忽略json [2]

  • 这个问题可以忽略json [3]
  • json [4]包含实际的更新数据。 稍后会详细介绍。
  • json [5]包含序列号。 如果它们无序到达,它将用于正确执行更新。 因此,如果我在1秒内按顺序1 - 3 - 5 - 4 - 2收到5次更新,则必须执行1 - 2 - 3 - 4 - 5 每个市场都有不同的“序列号序列”。

正如我所说, json [4]包含一系列更新。 json[4][array-index]["type"]有三种不同的类型:

  1. orderBookModify :特定价格的可用金额已更改。
  2. orderBookRemove :订单不再可用,必须删除。
  3. newTrade :可用于建立交易历史。 我不想要这样做,所以可以忽略它。

json[4][array-index]["data"]包含两个值(如果它是orderBookRemove)和三个值(如果它是orderBookModify)

  • :价格。
  • 金额(如果是订单,则仅存在)金额。
  • 类型询问出价

还有一种特殊的信息:

[36,8932491360003688,1315671639915103,{},[],{"seq":98045310}]

它只包含一个序列号。 它是一种心跳消息,不会发送任何更新。

编码

我用三个容器:

std::map<std::uint64_t,CMarket> m_mMarkets;
std::map<CMarket, long> m_mCurrentSeq;
std::map<CMarket, std::map<long, web::json::value>> m_mStack;

m_mMarkets用于将市场标识号映射到市场,因为它存储在我的程序中。

m_mCurrentSeq用于存储每个市场的当前序列号。

m_mStack按市场和序列号(这是long的)存储更新,直到它们可以被执行。

这是接收更新的部分:

// ....

// This method can be called asynchronously, so lock the containers.
this->m_muMutex.lock();

// Map the market-identifier to a CMarket object.
CMarket market = this->m_mMarkets.at(json[1].as_number().to_uint64());

// Check if it is a known market. This should never happen!
if(this->m_mMarkets.find(json[1].as_number().to_uint64()) == this->m_mMarkets.end())
{
    this->m_muMutex.unlock();
    throw std::runtime_error("Received Market update of unknown Market");
}

// Add the update to the execution-queue
this->m_mStack[market][(long)json[5]["seq"].as_integer()] = json;

// Execute the execution-queue
this->executeStack();

this->m_muMutex.unlock();

// ....

现在是执行队列。 我认为这是我的错误所在。

功能:“executeStack”:

for(auto& market : this->m_mMarkets) // For all markets
{
    if(this->m_mCurrentSeq.find(market.second) != this->m_mCurrentSeq.end()) // if market has a sequence number
    {
        long seqNum = this->m_mCurrentSeq.at(market.second);

        // erase old entries
        for(std::map<long, web::json::value>::iterator it = this->m_mStack.at(market.second).begin(); it != this->m_mStack.at(market.second).end(); )
        {
            if((*it).first < seqNum)
            it = this->m_mStack.at(market.second).erase(it);
            else
            ++it;
        }

        // This container is used to store the updates to the Orderbook temporarily.
        std::vector<Order> addOrderStack{};

        while(this->m_mStack.at(market.second).find(seqNum) != this->m_mStack.at(market.second).end())// has entry for seqNum
        {
            web::json::value json = this->m_mStack.at(market.second).at(seqNum);

            for(auto& v : json[4].as_array())
            {
                if(v["type"].as_string().compare("orderBookModify") == 0)
                {
                    Order::Type t = v["data"]["type"].as_string().compare("ask") == 0 ? Order::Type::Ask : Order::Type::Bid;
                    Order newOrder(std::stod(v["data"]["rate"].as_string()), std::stod(v["data"]["amount"].as_string()), t, market.second, this->m_pclParent, v.serialize());

                    addOrderStack.push_back(newOrder);

                } else if(v["type"].as_string().compare("orderBookRemove") == 0)
                {
                    Order::Type t = v["data"]["type"].as_string().compare("ask") == 0 ? Order::Type::Ask : Order::Type::Bid;
                    Order newOrder(std::stod(v["data"]["rate"].as_string()), 0, t, market.second, this->m_pclParent, v.serialize());

                    addOrderStack.push_back(newOrder);

                } else if(v["type"].as_string().compare("newTrade") == 0)
                {
                    //
                } else
                {
                    throw std::runtime_error("Unknown message format");
                }
            }

            this->m_mStack.at(market.second).erase(seqNum);
            seqNum++;
        }

        // The actual OrderList gets modified here. The mistake CANNOT be inside OrderList::addOrderStack, because I am running Orderbooks for other exchanges too and they use the same method to modify the Orderbook, and they do not get inconsistent.

        if(addOrderStack.size() > 0)
        OrderList::addOrderStack(addOrderStack);

        this->m_mCurrentSeq.at(market.second) = seqNum;

    }
}

因此,如果这种情况持续较长时间,则订单会变得不一致。 这意味着应该删除的订单仍然可用,并且书中有错误的内容。 我不太清楚为什么会这样。 也许我对序列号做了一些错误,因为似乎Update-Stack并不总能正确执行。 我已经尝试了一些我想到的东西,但我无法让它工作,现在我的想法可能是错的。 如果您有任何疑问,请随时提出。

tl; dr:Poloniex API不完善并丢弃消息。 有些根本就没到。 我发现这种情况发生在所有订阅的用户身上,无论他们身在何处。

希望关于利用Autobahn | cpp连接到Poloniex'Websocket API( 这里 )的答案是有用的。 我怀疑你已经弄明白了(否则这个问题/问题可能不适合你)。 您可能已经收集过,我也有一个用C ++编写的Crypto Currency Bot。 我一直在研究它已经有大约3。5年了。

你面临的问题是我必须克服的问题。 在这种情况下,我宁愿不提供我的源代码,因为您处理此代码的速度会对您的利润率产生巨大影响。 但是,我将提供sudo代码,这些代码可以非常粗略地了解我如何处理Poloniex的Web Socket事件处理。

//Sudo Code
void someClass::handle_poloniex_ws_event(ws_event event){
    if(event.seq_num == expected_seq_num){
       process_ws_event(event)
       update_expected_seq_num
    }
    else{
        if(in_cache(expected_seq_num){
            process_ws_event(from_cache(expected_seq_num))
            update_expected_seq_num
        }
        else{
            cache_event(event)
        }  
    }
} 

请注意,我上面写的是我实际做的超简化版本。 我的实际解决方案是大约500多行,其中包括“goto xxx”和“goto yyy”。 我建议采用时间戳/ cpu时钟周期计数并与当前时间/周期计数进行比较,以帮助您在任何给定时刻做出决策(例如,我应该等待丢失的事件,我应该继续处理并注意到程序的其余部分可能存在不准确之处,我是否应该使用GET请求来重新填充我的桌子等?)。 这里的游戏名称是速度,我相信你知道。 祝好运! 希望听到你的意见。 :-)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM