LCOV - code coverage report
Current view: top level - src/app/engine - order_book.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 89.4 % 357 319
Test Date: 2025-12-19 03:13:09 Functions: 100.0 % 16 16

            Line data    Source code
       1              : /**
       2              :  * @file order_book.cpp
       3              :  * @brief 订单簿实现
       4              :  */
       5              : 
       6              : #include "app/engine/order_book.hpp"
       7              : #include "base/logger.hpp"
       8              : #include <sstream>
       9              : #include <iomanip>
      10              : 
      11              : namespace fix40 {
      12              : 
      13           50 : OrderBook::OrderBook(const std::string& symbol)
      14           50 :     : symbol_(symbol)
      15              : {
      16           50 : }
      17              : 
      18           89 : std::string OrderBook::generateOrderID() {
      19           89 :     std::ostringstream oss;
      20           89 :     oss << symbol_ << "-" << std::setfill('0') << std::setw(8) << nextOrderID_++;
      21          178 :     return oss.str();
      22           89 : }
      23              : 
      24           36 : std::string OrderBook::generateTradeID() {
      25           36 :     std::ostringstream oss;
      26           36 :     oss << symbol_ << "-T" << std::setfill('0') << std::setw(8) << nextTradeID_++;
      27           72 :     return oss.str();
      28           36 : }
      29              : 
      30           97 : std::vector<Trade> OrderBook::addOrder(Order& order) {
      31              :     // 输入校验
      32           97 :     if (order.orderQty <= 0) {
      33            2 :         LOG() << "[OrderBook:" << symbol_ << "] Rejected: invalid orderQty " << order.orderQty;
      34            2 :         order.status = OrderStatus::REJECTED;
      35            2 :         return {};
      36              :     }
      37           95 :     if (order.leavesQty <= 0) {
      38            0 :         LOG() << "[OrderBook:" << symbol_ << "] Rejected: invalid leavesQty " << order.leavesQty;
      39            0 :         order.status = OrderStatus::REJECTED;
      40            0 :         return {};
      41              :     }
      42           95 :     if (order.ordType == OrderType::LIMIT && order.price <= 0) {
      43            2 :         LOG() << "[OrderBook:" << symbol_ << "] Rejected: invalid price " << order.price << " for limit order";
      44            2 :         order.status = OrderStatus::REJECTED;
      45            2 :         return {};
      46              :     }
      47           93 :     if (order.symbol != symbol_) {
      48            1 :         LOG() << "[OrderBook:" << symbol_ << "] Rejected: symbol mismatch " << order.symbol;
      49            1 :         order.status = OrderStatus::REJECTED;
      50            1 :         return {};
      51              :     }
      52           92 :     if (order.clOrdID.empty()) {
      53            1 :         LOG() << "[OrderBook:" << symbol_ << "] Rejected: empty clOrdID";
      54            1 :         order.status = OrderStatus::REJECTED;
      55            1 :         return {};
      56              :     }
      57              :     
      58              :     // 市价单检查:必须有对手盘
      59           91 :     if (order.ordType == OrderType::MARKET) {
      60           12 :         bool hasCounterparty = (order.side == OrderSide::BUY) ? !asks_.empty() : !bids_.empty();
      61           12 :         if (!hasCounterparty) {
      62            2 :             LOG() << "[OrderBook:" << symbol_ << "] Rejected: market order with no counterparty";
      63            2 :             order.status = OrderStatus::REJECTED;
      64            2 :             return {};
      65              :         }
      66              :     }
      67              :     
      68              :     // 生成订单ID
      69           89 :     order.orderID = generateOrderID();
      70           89 :     order.updateTime = std::chrono::system_clock::now();
      71              :     
      72           89 :     const char* ordTypeStr = (order.ordType == OrderType::MARKET) ? "MARKET" : "LIMIT";
      73           89 :     LOG() << "[OrderBook:" << symbol_ << "] Adding " << ordTypeStr << " order: " 
      74           89 :           << order.clOrdID << " " << (order.side == OrderSide::BUY ? "BUY" : "SELL")
      75           89 :           << " " << order.orderQty 
      76          109 :           << (order.ordType == OrderType::LIMIT ? " @ " + std::to_string(order.price) : "");
      77              : 
      78              :     // FOK 订单预检查:必须能全部成交
      79           89 :     if (order.timeInForce == TimeInForce::FOK) {
      80            8 :         int64_t availableQty = calculateMatchableQty(order);
      81            8 :         if (availableQty < order.orderQty) {
      82            8 :             LOG() << "[OrderBook:" << symbol_ << "] FOK order " << order.clOrdID 
      83            4 :                   << " rejected: available " << availableQty << " < required " << order.orderQty;
      84            4 :             order.status = OrderStatus::REJECTED;
      85            4 :             return {};
      86              :         }
      87              :     }
      88              : 
      89           85 :     std::vector<Trade> trades;
      90              :     
      91              :     // 根据买卖方向撮合
      92           85 :     if (order.side == OrderSide::BUY) {
      93           40 :         trades = matchBuyOrder(order);
      94              :     } else {
      95           45 :         trades = matchSellOrder(order);
      96              :     }
      97              : 
      98              :     // 处理剩余数量
      99           85 :     if (order.leavesQty > 0 && !order.isTerminal()) {
     100           67 :         bool shouldCancelRemaining = false;
     101           67 :         std::string cancelReason;
     102              :         
     103              :         // FOK 订单不应该走到这里(预检查应该已经拒绝)
     104           67 :         if (order.timeInForce == TimeInForce::FOK) {
     105              :             // 这是一个实现错误:FOK 预检查通过但未能全部成交
     106            0 :             LOG() << "[OrderBook:" << symbol_ << "] ERROR: FOK order " << order.clOrdID 
     107            0 :                   << " passed pre-check but has remaining qty " << order.leavesQty
     108            0 :                   << " - this indicates a bug in calculateMatchableQty";
     109            0 :             shouldCancelRemaining = true;
     110            0 :             cancelReason = "FOK implementation error";
     111           67 :         } else if (order.timeInForce == TimeInForce::IOC) {
     112              :             // IOC: 立即成交否则取消(优先于市价单判断,因为 Market+IOC 应显示 IOC)
     113            6 :             shouldCancelRemaining = true;
     114            6 :             cancelReason = (order.ordType == OrderType::MARKET) ? "Market IOC" : "IOC";
     115           61 :         } else if (order.ordType == OrderType::MARKET) {
     116              :             // 市价单:未成交部分取消
     117            2 :             shouldCancelRemaining = true;
     118            2 :             cancelReason = "no more counterparty";
     119              :         } else {
     120              :             // DAY/GTC: 限价单挂入订单簿
     121           59 :             if (order.side == OrderSide::BUY) {
     122           20 :                 addToBids(order);
     123              :             } else {
     124           39 :                 addToAsks(order);
     125              :             }
     126              :             
     127              :             // 更新订单状态
     128           59 :             if (order.cumQty == 0) {
     129           58 :                 order.status = OrderStatus::NEW;
     130              :             }
     131              :             // 部分成交状态在撮合时已设置
     132              :         }
     133              :         
     134           67 :         if (shouldCancelRemaining) {
     135            8 :             bool partialFilled = (order.cumQty > 0);
     136            8 :             order.status = partialFilled ? OrderStatus::CANCELED : OrderStatus::REJECTED;
     137           16 :             LOG() << "[OrderBook:" << symbol_ << "] Order " << order.clOrdID 
     138            8 :                   << " remaining " << order.leavesQty 
     139            8 :                   << (partialFilled ? " canceled" : " rejected") 
     140            8 :                   << " (" << cancelReason << ")";
     141              :         }
     142           67 :     }
     143              : 
     144           85 :     return trades;
     145           85 : }
     146              : 
     147           40 : std::vector<Trade> OrderBook::matchBuyOrder(Order& buyOrder) {
     148           40 :     std::vector<Trade> trades;
     149              :     
     150              :     // 遍历卖盘(从最低价开始)
     151           40 :     auto it = asks_.begin();
     152           68 :     while (it != asks_.end() && buyOrder.leavesQty > 0) {
     153           29 :         PriceLevel& level = it->second;
     154              :         
     155              :         // 检查价格是否可以成交
     156              :         // 市价单:不检查价格,直接成交
     157              :         // 限价单:买价 >= 卖价 才能成交
     158           29 :         if (buyOrder.ordType == OrderType::LIMIT && buyOrder.price < level.price) {
     159            1 :             break;  // 后面的卖价更高,不可能成交
     160              :         }
     161              : 
     162              :         // 遍历该价位的订单
     163           28 :         auto orderIt = level.orders.begin();
     164           56 :         while (orderIt != level.orders.end() && buyOrder.leavesQty > 0) {
     165           28 :             Order& sellOrder = *orderIt;
     166              :             
     167              :             // 计算成交数量
     168           28 :             int64_t matchQty = std::min(buyOrder.leavesQty, sellOrder.leavesQty);
     169           28 :             double matchPrice = sellOrder.price;  // 成交价取被动方价格
     170              :             
     171              :             // 更新买单
     172           28 :             buyOrder.cumQty += matchQty;
     173           28 :             buyOrder.leavesQty -= matchQty;
     174           28 :             buyOrder.avgPx = (buyOrder.avgPx * (buyOrder.cumQty - matchQty) + matchPrice * matchQty) 
     175           28 :                            / buyOrder.cumQty;
     176           28 :             auto now = std::chrono::system_clock::now();
     177           28 :             buyOrder.updateTime = now;
     178              :             
     179           28 :             if (buyOrder.leavesQty == 0) {
     180           13 :                 buyOrder.status = OrderStatus::FILLED;
     181              :             } else {
     182           15 :                 buyOrder.status = OrderStatus::PARTIALLY_FILLED;
     183              :             }
     184              : 
     185              :             // 更新卖单
     186           28 :             sellOrder.cumQty += matchQty;
     187           28 :             sellOrder.leavesQty -= matchQty;
     188           28 :             sellOrder.avgPx = (sellOrder.avgPx * (sellOrder.cumQty - matchQty) + matchPrice * matchQty)
     189           28 :                             / sellOrder.cumQty;
     190           28 :             sellOrder.updateTime = now;
     191              :             
     192           28 :             if (sellOrder.leavesQty == 0) {
     193           26 :                 sellOrder.status = OrderStatus::FILLED;
     194              :             } else {
     195            2 :                 sellOrder.status = OrderStatus::PARTIALLY_FILLED;
     196              :             }
     197              : 
     198              :             // 创建成交记录(包含完整的双方订单快照)
     199           28 :             Trade trade;
     200           28 :             trade.tradeID = generateTradeID();
     201           28 :             trade.symbol = symbol_;
     202           28 :             trade.price = matchPrice;
     203           28 :             trade.qty = matchQty;
     204           28 :             trade.timestamp = now;
     205              :             
     206              :             // 买方信息
     207           28 :             trade.buyOrderID = buyOrder.orderID;
     208           28 :             trade.buyClOrdID = buyOrder.clOrdID;
     209           28 :             trade.buyOrderQty = buyOrder.orderQty;
     210           28 :             trade.buyPrice = buyOrder.price;
     211           28 :             trade.buyOrdType = buyOrder.ordType;
     212           28 :             trade.buyCumQty = buyOrder.cumQty;
     213           28 :             trade.buyLeavesQty = buyOrder.leavesQty;
     214           28 :             trade.buyAvgPx = buyOrder.avgPx;
     215           28 :             trade.buyStatus = buyOrder.status;
     216              :             
     217              :             // 卖方信息
     218           28 :             trade.sellOrderID = sellOrder.orderID;
     219           28 :             trade.sellClOrdID = sellOrder.clOrdID;
     220           28 :             trade.sellOrderQty = sellOrder.orderQty;
     221           28 :             trade.sellPrice = sellOrder.price;
     222           28 :             trade.sellOrdType = sellOrder.ordType;
     223           28 :             trade.sellCumQty = sellOrder.cumQty;
     224           28 :             trade.sellLeavesQty = sellOrder.leavesQty;
     225           28 :             trade.sellAvgPx = sellOrder.avgPx;
     226           28 :             trade.sellStatus = sellOrder.status;
     227              :             
     228           28 :             trades.push_back(trade);
     229              :             
     230           56 :             LOG() << "[OrderBook:" << symbol_ << "] Trade: " << trade.tradeID
     231           28 :                   << " " << matchQty << " @ " << matchPrice
     232           28 :                   << " (Buy:" << buyOrder.clOrdID << " Sell:" << sellOrder.clOrdID << ")";
     233              :             
     234           28 :             if (sellOrder.leavesQty == 0) {
     235              :                 // 从索引中移除
     236           26 :                 orderIndex_.erase(sellOrder.clOrdID);
     237           26 :                 orderIt = level.orders.erase(orderIt);
     238           26 :                 askOrderCount_--;
     239              :             } else {
     240            2 :                 ++orderIt;
     241              :             }
     242              :             
     243           28 :             level.totalQty -= matchQty;
     244           28 :         }
     245              : 
     246              :         // 如果该价位已空,移除
     247           28 :         if (level.orders.empty()) {
     248           25 :             it = asks_.erase(it);
     249              :         } else {
     250            3 :             ++it;
     251              :         }
     252              :     }
     253              : 
     254           80 :     return trades;
     255            0 : }
     256              : 
     257           45 : std::vector<Trade> OrderBook::matchSellOrder(Order& sellOrder) {
     258           45 :     std::vector<Trade> trades;
     259              :     
     260              :     // 遍历买盘(从最高价开始)
     261           45 :     auto it = bids_.begin();
     262           53 :     while (it != bids_.end() && sellOrder.leavesQty > 0) {
     263           11 :         PriceLevel& level = it->second;
     264              :         
     265              :         // 检查价格是否可以成交
     266              :         // 市价单:不检查价格,直接成交
     267              :         // 限价单:卖价 <= 买价 才能成交
     268           11 :         if (sellOrder.ordType == OrderType::LIMIT && sellOrder.price > level.price) {
     269            3 :             break;  // 后面的买价更低,不可能成交
     270              :         }
     271              : 
     272              :         // 遍历该价位的订单
     273            8 :         auto orderIt = level.orders.begin();
     274           16 :         while (orderIt != level.orders.end() && sellOrder.leavesQty > 0) {
     275            8 :             Order& buyOrder = *orderIt;
     276              :             
     277              :             // 计算成交数量
     278            8 :             int64_t matchQty = std::min(sellOrder.leavesQty, buyOrder.leavesQty);
     279            8 :             double matchPrice = buyOrder.price;  // 成交价取被动方价格
     280              :             
     281              :             // 更新卖单
     282            8 :             sellOrder.cumQty += matchQty;
     283            8 :             sellOrder.leavesQty -= matchQty;
     284            8 :             sellOrder.avgPx = (sellOrder.avgPx * (sellOrder.cumQty - matchQty) + matchPrice * matchQty)
     285            8 :                             / sellOrder.cumQty;
     286            8 :             auto now = std::chrono::system_clock::now();
     287            8 :             sellOrder.updateTime = now;
     288              :             
     289            8 :             if (sellOrder.leavesQty == 0) {
     290            5 :                 sellOrder.status = OrderStatus::FILLED;
     291              :             } else {
     292            3 :                 sellOrder.status = OrderStatus::PARTIALLY_FILLED;
     293              :             }
     294              : 
     295              :             // 更新买单
     296            8 :             buyOrder.cumQty += matchQty;
     297            8 :             buyOrder.leavesQty -= matchQty;
     298            8 :             buyOrder.avgPx = (buyOrder.avgPx * (buyOrder.cumQty - matchQty) + matchPrice * matchQty)
     299            8 :                            / buyOrder.cumQty;
     300            8 :             buyOrder.updateTime = now;
     301              :             
     302            8 :             if (buyOrder.leavesQty == 0) {
     303            7 :                 buyOrder.status = OrderStatus::FILLED;
     304              :             } else {
     305            1 :                 buyOrder.status = OrderStatus::PARTIALLY_FILLED;
     306              :             }
     307              : 
     308              :             // 创建成交记录(包含完整的双方订单快照)
     309            8 :             Trade trade;
     310            8 :             trade.tradeID = generateTradeID();
     311            8 :             trade.symbol = symbol_;
     312            8 :             trade.price = matchPrice;
     313            8 :             trade.qty = matchQty;
     314            8 :             trade.timestamp = now;
     315              :             
     316              :             // 买方信息
     317            8 :             trade.buyOrderID = buyOrder.orderID;
     318            8 :             trade.buyClOrdID = buyOrder.clOrdID;
     319            8 :             trade.buyOrderQty = buyOrder.orderQty;
     320            8 :             trade.buyPrice = buyOrder.price;
     321            8 :             trade.buyOrdType = buyOrder.ordType;
     322            8 :             trade.buyCumQty = buyOrder.cumQty;
     323            8 :             trade.buyLeavesQty = buyOrder.leavesQty;
     324            8 :             trade.buyAvgPx = buyOrder.avgPx;
     325            8 :             trade.buyStatus = buyOrder.status;
     326              :             
     327              :             // 卖方信息
     328            8 :             trade.sellOrderID = sellOrder.orderID;
     329            8 :             trade.sellClOrdID = sellOrder.clOrdID;
     330            8 :             trade.sellOrderQty = sellOrder.orderQty;
     331            8 :             trade.sellPrice = sellOrder.price;
     332            8 :             trade.sellOrdType = sellOrder.ordType;
     333            8 :             trade.sellCumQty = sellOrder.cumQty;
     334            8 :             trade.sellLeavesQty = sellOrder.leavesQty;
     335            8 :             trade.sellAvgPx = sellOrder.avgPx;
     336            8 :             trade.sellStatus = sellOrder.status;
     337              :             
     338            8 :             trades.push_back(trade);
     339              :             
     340           16 :             LOG() << "[OrderBook:" << symbol_ << "] Trade: " << trade.tradeID
     341            8 :                   << " " << matchQty << " @ " << matchPrice
     342            8 :                   << " (Buy:" << buyOrder.clOrdID << " Sell:" << sellOrder.clOrdID << ")";
     343              :             
     344            8 :             if (buyOrder.leavesQty == 0) {
     345              :                 // 从索引中移除
     346            7 :                 orderIndex_.erase(buyOrder.clOrdID);
     347            7 :                 orderIt = level.orders.erase(orderIt);
     348            7 :                 bidOrderCount_--;
     349              :             } else {
     350            1 :                 ++orderIt;
     351              :             }
     352              :             
     353            8 :             level.totalQty -= matchQty;
     354            8 :         }
     355              : 
     356              :         // 如果该价位已空,移除
     357            8 :         if (level.orders.empty()) {
     358            7 :             it = bids_.erase(it);
     359              :         } else {
     360            1 :             ++it;
     361              :         }
     362              :     }
     363              : 
     364           90 :     return trades;
     365            0 : }
     366              : 
     367           20 : void OrderBook::addToBids(const Order& order) {
     368           20 :     auto& level = bids_[order.price];
     369           20 :     if (level.orders.empty()) {
     370           19 :         level.price = order.price;
     371              :     }
     372           20 :     level.orders.push_back(order);
     373           20 :     level.totalQty += order.leavesQty;
     374              :     
     375           20 :     orderIndex_[order.clOrdID] = {OrderSide::BUY, order.price};
     376           20 :     bidOrderCount_++;
     377              :     
     378           40 :     LOG() << "[OrderBook:" << symbol_ << "] Order " << order.clOrdID 
     379           20 :           << " added to bids @ " << order.price;
     380           20 : }
     381              : 
     382           39 : void OrderBook::addToAsks(const Order& order) {
     383           39 :     auto& level = asks_[order.price];
     384           39 :     if (level.orders.empty()) {
     385           38 :         level.price = order.price;
     386              :     }
     387           39 :     level.orders.push_back(order);
     388           39 :     level.totalQty += order.leavesQty;
     389              :     
     390           39 :     orderIndex_[order.clOrdID] = {OrderSide::SELL, order.price};
     391           39 :     askOrderCount_++;
     392              :     
     393           78 :     LOG() << "[OrderBook:" << symbol_ << "] Order " << order.clOrdID 
     394           39 :           << " added to asks @ " << order.price;
     395           39 : }
     396              : 
     397            2 : std::optional<Order> OrderBook::cancelOrder(const std::string& clOrdID) {
     398            2 :     auto indexIt = orderIndex_.find(clOrdID);
     399            2 :     if (indexIt == orderIndex_.end()) {
     400            1 :         LOG() << "[OrderBook:" << symbol_ << "] Cancel failed: order " << clOrdID << " not found";
     401            1 :         return std::nullopt;
     402              :     }
     403              :     
     404            1 :     return removeOrder(clOrdID, indexIt->second.side);
     405              : }
     406              : 
     407            1 : std::optional<Order> OrderBook::removeOrder(const std::string& clOrdID, OrderSide side) {
     408            1 :     auto indexIt = orderIndex_.find(clOrdID);
     409            1 :     if (indexIt == orderIndex_.end()) {
     410            0 :         return std::nullopt;
     411              :     }
     412              :     
     413            1 :     double price = indexIt->second.price;
     414              :     
     415            1 :     if (side == OrderSide::BUY) {
     416            1 :         auto levelIt = bids_.find(price);
     417            1 :         if (levelIt != bids_.end()) {
     418            1 :             auto& orders = levelIt->second.orders;
     419            1 :             for (auto it = orders.begin(); it != orders.end(); ++it) {
     420            1 :                 if (it->clOrdID == clOrdID) {
     421            1 :                     Order canceledOrder = *it;
     422            1 :                     canceledOrder.status = OrderStatus::CANCELED;
     423            1 :                     canceledOrder.updateTime = std::chrono::system_clock::now();
     424              :                     
     425            1 :                     levelIt->second.totalQty -= it->leavesQty;
     426            1 :                     orders.erase(it);
     427            1 :                     bidOrderCount_--;
     428              :                     
     429            1 :                     if (orders.empty()) {
     430            1 :                         bids_.erase(levelIt);
     431              :                     }
     432              :                     
     433            1 :                     orderIndex_.erase(clOrdID);
     434              :                     
     435            1 :                     LOG() << "[OrderBook:" << symbol_ << "] Order " << clOrdID << " canceled";
     436            1 :                     return canceledOrder;
     437            1 :                 }
     438              :             }
     439              :         }
     440              :     } else {
     441            0 :         auto levelIt = asks_.find(price);
     442            0 :         if (levelIt != asks_.end()) {
     443            0 :             auto& orders = levelIt->second.orders;
     444            0 :             for (auto it = orders.begin(); it != orders.end(); ++it) {
     445            0 :                 if (it->clOrdID == clOrdID) {
     446            0 :                     Order canceledOrder = *it;
     447            0 :                     canceledOrder.status = OrderStatus::CANCELED;
     448            0 :                     canceledOrder.updateTime = std::chrono::system_clock::now();
     449              :                     
     450            0 :                     levelIt->second.totalQty -= it->leavesQty;
     451            0 :                     orders.erase(it);
     452            0 :                     askOrderCount_--;
     453              :                     
     454            0 :                     if (orders.empty()) {
     455            0 :                         asks_.erase(levelIt);
     456              :                     }
     457              :                     
     458            0 :                     orderIndex_.erase(clOrdID);
     459              :                     
     460            0 :                     LOG() << "[OrderBook:" << symbol_ << "] Order " << clOrdID << " canceled";
     461            0 :                     return canceledOrder;
     462            0 :                 }
     463              :             }
     464              :         }
     465              :     }
     466              :     
     467            0 :     return std::nullopt;
     468              : }
     469              : 
     470            3 : const Order* OrderBook::findOrder(const std::string& clOrdID) const {
     471            3 :     auto indexIt = orderIndex_.find(clOrdID);
     472            3 :     if (indexIt == orderIndex_.end()) {
     473            2 :         return nullptr;
     474              :     }
     475              :     
     476            1 :     double price = indexIt->second.price;
     477            1 :     OrderSide side = indexIt->second.side;
     478              :     
     479            1 :     if (side == OrderSide::BUY) {
     480            1 :         auto levelIt = bids_.find(price);
     481            1 :         if (levelIt != bids_.end()) {
     482            1 :             for (const auto& order : levelIt->second.orders) {
     483            1 :                 if (order.clOrdID == clOrdID) {
     484            1 :                     return &order;
     485              :                 }
     486              :             }
     487              :         }
     488              :     } else {
     489            0 :         auto levelIt = asks_.find(price);
     490            0 :         if (levelIt != asks_.end()) {
     491            0 :             for (const auto& order : levelIt->second.orders) {
     492            0 :                 if (order.clOrdID == clOrdID) {
     493            0 :                     return &order;
     494              :                 }
     495              :             }
     496              :         }
     497              :     }
     498              :     
     499            0 :     return nullptr;
     500              : }
     501              : 
     502            3 : std::optional<double> OrderBook::getBestBid() const {
     503            3 :     if (bids_.empty()) {
     504            1 :         return std::nullopt;
     505              :     }
     506            2 :     return bids_.begin()->first;
     507              : }
     508              : 
     509            4 : std::optional<double> OrderBook::getBestAsk() const {
     510            4 :     if (asks_.empty()) {
     511            1 :         return std::nullopt;
     512              :     }
     513            3 :     return asks_.begin()->first;
     514              : }
     515              : 
     516            1 : std::vector<PriceLevel> OrderBook::getBidLevels(size_t levels) const {
     517            1 :     std::vector<PriceLevel> result;
     518            1 :     result.reserve(levels);
     519              :     
     520            1 :     size_t count = 0;
     521            4 :     for (const auto& [price, level] : bids_) {
     522            3 :         if (count >= levels) break;
     523            3 :         result.push_back(level);
     524            3 :         count++;
     525              :     }
     526              :     
     527            1 :     return result;
     528            0 : }
     529              : 
     530            1 : std::vector<PriceLevel> OrderBook::getAskLevels(size_t levels) const {
     531            1 :     std::vector<PriceLevel> result;
     532            1 :     result.reserve(levels);
     533              :     
     534            1 :     size_t count = 0;
     535            3 :     for (const auto& [price, level] : asks_) {
     536            2 :         if (count >= levels) break;
     537            2 :         result.push_back(level);
     538            2 :         count++;
     539              :     }
     540              :     
     541            1 :     return result;
     542            0 : }
     543              : 
     544            8 : int64_t OrderBook::calculateMatchableQty(const Order& order) const {
     545            8 :     int64_t matchableQty = 0;
     546              :     
     547            8 :     if (order.side == OrderSide::BUY) {
     548              :         // 买单:遍历卖盘计算可成交数量
     549           11 :         for (const auto& [askPrice, level] : asks_) {
     550              :             // 市价单不检查价格,限价单检查价格
     551            8 :             if (order.ordType == OrderType::LIMIT && order.price < askPrice) {
     552            1 :                 break;  // 后面的卖价更高,不可能成交
     553              :             }
     554            7 :             matchableQty += level.totalQty;
     555            7 :             if (matchableQty >= order.orderQty) {
     556            3 :                 return order.orderQty;  // 已经足够
     557              :             }
     558              :         }
     559              :     } else {
     560              :         // 卖单:遍历买盘计算可成交数量
     561            1 :         for (const auto& [bidPrice, level] : bids_) {
     562              :             // 市价单不检查价格,限价单检查价格
     563            1 :             if (order.ordType == OrderType::LIMIT && order.price > bidPrice) {
     564            0 :                 break;  // 后面的买价更低,不可能成交
     565              :             }
     566            1 :             matchableQty += level.totalQty;
     567            1 :             if (matchableQty >= order.orderQty) {
     568            1 :                 return order.orderQty;  // 已经足够
     569              :             }
     570              :         }
     571              :     }
     572              :     
     573            4 :     return matchableQty;
     574              : }
     575              : 
     576              : } // namespace fix40
        

Generated by: LCOV version 2.0-1