LCOV - code coverage report
Current view: top level - src/app - simulation_app.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 61.7 % 669 413
Test Date: 2025-12-19 03:13:09 Functions: 83.8 % 37 31

            Line data    Source code
       1              : /**
       2              :  * @file simulation_app.cpp
       3              :  * @brief 模拟交易应用层实现
       4              :  * 
       5              :  * 集成AccountManager、PositionManager、InstrumentManager、RiskManager,
       6              :  * 实现完整的模拟交易流程:
       7              :  * 1. 接收FIX订单消息
       8              :  * 2. 风控检查
       9              :  * 3. 提交撮合引擎
      10              :  * 4. 处理成交回报,更新账户和持仓
      11              :  */
      12              : 
      13              : #include "app/simulation_app.hpp"
      14              : #include "fix/fix_message_builder.hpp"
      15              : #include "fix/fix_tags.hpp"
      16              : #include "base/logger.hpp"
      17              : #include "storage/store.hpp"
      18              : #include <cstdlib>
      19              : #include <sstream>
      20              : #include <iomanip>
      21              : #include <set>
      22              : #include <optional>
      23              : #include <algorithm>
      24              : 
      25              : namespace fix40 {
      26              : 
      27              : // ============================================================================
      28              : // FIX 消息解析辅助函数
      29              : // ============================================================================
      30              : 
      31              : namespace {
      32              : 
      33              : /**
      34              :  * @brief 将系统时间转换为 epoch 毫秒时间戳
      35              :  */
      36            1 : int64_t to_epoch_millis(std::chrono::system_clock::time_point tp) {
      37            1 :     return std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch()).count();
      38              : }
      39              : 
      40              : /**
      41              :  * @brief 解析结果
      42              :  */
      43              : struct ParseResult {
      44              :     bool success = false;
      45              :     std::string error;
      46              :     Order order;
      47              : };
      48              : 
      49              : /**
      50              :  * @brief 从 FIX 消息解析 Order 结构
      51              :  * @return ParseResult 包含解析结果和错误信息
      52              :  */
      53            5 : ParseResult parseNewOrderSingle(const FixMessage& msg, const SessionID& sessionID) {
      54            5 :     ParseResult result;
      55            5 :     Order& order = result.order;
      56            5 :     order.sessionID = sessionID;
      57              :     
      58              :     // 必填字段:ClOrdID
      59            5 :     if (!msg.has(tags::ClOrdID)) {
      60            0 :         result.error = "Missing required field: ClOrdID(11)";
      61            0 :         return result;
      62              :     }
      63            5 :     order.clOrdID = msg.get_string(tags::ClOrdID);
      64              :     
      65              :     // 必填字段:Symbol
      66            5 :     if (!msg.has(tags::Symbol)) {
      67            0 :         result.error = "Missing required field: Symbol(55)";
      68            0 :         return result;
      69              :     }
      70            5 :     order.symbol = msg.get_string(tags::Symbol);
      71              :     
      72              :     // 必填字段:Side (1=Buy, 2=Sell)
      73            5 :     if (!msg.has(tags::Side)) {
      74            0 :         result.error = "Missing required field: Side(54)";
      75            0 :         return result;
      76              :     }
      77            5 :     std::string sideStr = msg.get_string(tags::Side);
      78            5 :     if (sideStr == "1") {
      79            5 :         order.side = OrderSide::BUY;
      80            0 :     } else if (sideStr == "2") {
      81            0 :         order.side = OrderSide::SELL;
      82              :     } else {
      83            0 :         result.error = "Invalid Side(54) value: " + sideStr + " (expected 1 or 2)";
      84            0 :         return result;
      85              :     }
      86              :     
      87              :     // 必填字段:OrderQty
      88            5 :     if (!msg.has(tags::OrderQty)) {
      89            0 :         result.error = "Missing required field: OrderQty(38)";
      90            0 :         return result;
      91              :     }
      92              :     try {
      93            5 :         order.orderQty = std::stoll(msg.get_string(tags::OrderQty));
      94            5 :         if (order.orderQty <= 0) {
      95            0 :             result.error = "Invalid OrderQty(38): must be positive";
      96            0 :             return result;
      97              :         }
      98            0 :     } catch (const std::exception& e) {
      99            0 :         result.error = "Invalid OrderQty(38) format: " + std::string(e.what());
     100            0 :         return result;
     101            0 :     }
     102              :     
     103              :     // 必填字段:OrdType (1=Market, 2=Limit)
     104            5 :     if (!msg.has(tags::OrdType)) {
     105            0 :         result.error = "Missing required field: OrdType(40)";
     106            0 :         return result;
     107              :     }
     108            5 :     std::string ordTypeStr = msg.get_string(tags::OrdType);
     109            5 :     if (ordTypeStr == "1") {
     110            0 :         order.ordType = OrderType::MARKET;
     111            5 :     } else if (ordTypeStr == "2") {
     112            5 :         order.ordType = OrderType::LIMIT;
     113              :     } else {
     114            0 :         LOG() << "[SimulationApp] Warning: Unknown OrdType(40)=" << ordTypeStr << ", defaulting to LIMIT";
     115            0 :         order.ordType = OrderType::LIMIT;
     116              :     }
     117              :     
     118              :     // Price (限价单必填)
     119            5 :     if (msg.has(tags::Price)) {
     120              :         try {
     121            5 :             order.price = std::stod(msg.get_string(tags::Price));
     122            0 :         } catch (const std::exception& e) {
     123            0 :             result.error = "Invalid Price(44) format: " + std::string(e.what());
     124            0 :             return result;
     125            0 :         }
     126            0 :     } else if (order.ordType == OrderType::LIMIT) {
     127            0 :         result.error = "Missing required field: Price(44) for limit order";
     128            0 :         return result;
     129              :     }
     130              :     
     131              :     // TimeInForce: 0=Day, 1=GTC, 3=IOC, 4=FOK (可选,默认 DAY)
     132            5 :     if (msg.has(tags::TimeInForce)) {
     133            0 :         std::string tifStr = msg.get_string(tags::TimeInForce);
     134            0 :         if (tifStr == "0") order.timeInForce = TimeInForce::DAY;
     135            0 :         else if (tifStr == "1") order.timeInForce = TimeInForce::GTC;
     136            0 :         else if (tifStr == "3") order.timeInForce = TimeInForce::IOC;
     137            0 :         else if (tifStr == "4") order.timeInForce = TimeInForce::FOK;
     138              :         else {
     139            0 :             LOG() << "[SimulationApp] Warning: Unknown TimeInForce(59)=" << tifStr << ", defaulting to DAY";
     140            0 :             order.timeInForce = TimeInForce::DAY;
     141              :         }
     142            0 :     }
     143              :     
     144              :     // 初始化执行状态
     145            5 :     order.status = OrderStatus::PENDING_NEW;
     146            5 :     order.cumQty = 0;
     147            5 :     order.leavesQty = order.orderQty;
     148            5 :     order.avgPx = 0.0;
     149            5 :     order.createTime = std::chrono::system_clock::now();
     150            5 :     order.updateTime = order.createTime;
     151              :     
     152            5 :     result.success = true;
     153            5 :     return result;
     154            5 : }
     155              : 
     156              : /**
     157              :  * @brief 从 FIX 消息解析 CancelRequest 结构
     158              :  */
     159            1 : CancelRequest parseCancelRequest(const FixMessage& msg, const SessionID& sessionID) {
     160            1 :     CancelRequest req;
     161            1 :     req.sessionID = sessionID;
     162              :     
     163            1 :     req.clOrdID = msg.get_string(tags::ClOrdID);
     164            1 :     req.origClOrdID = msg.get_string(tags::OrigClOrdID);
     165              :     
     166            1 :     if (msg.has(tags::Symbol)) {
     167            1 :         req.symbol = msg.get_string(tags::Symbol);
     168              :     }
     169              :     
     170            1 :     return req;
     171            0 : }
     172              : 
     173              : } // anonymous namespace
     174              : 
     175              : // ============================================================================
     176              : // SimulationApp 实现
     177              : // ============================================================================
     178              : 
     179           16 : SimulationApp::SimulationApp()
     180           16 :     : store_(nullptr) {
     181           16 :     initializeManagers();
     182           16 : }
     183              : 
     184            4 : SimulationApp::SimulationApp(IStore* store)
     185            4 :     : accountManager_(store)
     186            4 :     , positionManager_(store)
     187            8 :     , store_(store) {
     188            4 :     initializeManagers();
     189            4 : }
     190              : 
     191           20 : void SimulationApp::initializeManagers() {
     192              :     // 撮合引擎只负责撮合与订单状态推进;账户/持仓/风控等业务权威逻辑由 SimulationApp 负责。
     193              :     // 撮合引擎仅需要合约信息用于行情驱动撮合相关的辅助更新(如涨跌停价格)。
     194           20 :     engine_.setInstrumentManager(&instrumentManager_);
     195              :     
     196              :     // 设置 ExecutionReport 回调
     197           20 :     engine_.setExecutionReportCallback(
     198           20 :         [this](const SessionID& sid, const ExecutionReport& rpt) {
     199            2 :             onExecutionReport(sid, rpt);
     200            2 :         });
     201              :     
     202              :     // 设置行情更新回调(用于账户价值重算和推送)
     203           20 :     engine_.setMarketDataUpdateCallback(
     204           20 :         [this](const std::string& instrumentId, double lastPrice) {
     205            2 :             onMarketDataUpdate(instrumentId, lastPrice);
     206            2 :         });
     207           20 : }
     208              : 
     209           20 : SimulationApp::~SimulationApp() {
     210           20 :     stop();
     211           20 : }
     212              : 
     213           10 : void SimulationApp::start() {
     214           10 :     engine_.start();
     215           10 : }
     216              : 
     217           30 : void SimulationApp::stop() {
     218           30 :     engine_.stop();
     219           30 : }
     220              : 
     221            1 : void SimulationApp::onLogon(const SessionID& sessionID) {
     222              :     // 身份绑定:使用 SenderCompID 作为用户ID
     223            1 :     std::string userId = extractAccountId(sessionID);
     224              :     
     225              :     // 安全检查:拒绝无效的用户ID
     226            1 :     if (userId.empty()) {
     227            1 :         LOG() << "[SimulationApp] Rejecting logon: invalid user ID for session "
     228            1 :               << sessionID.to_string();
     229            1 :         return;
     230              :     }
     231              :     
     232            0 :     LOG() << "[SimulationApp] Session logged on: " << sessionID.to_string()
     233            0 :           << " -> User: " << userId;
     234              :     
     235              :     // 自动开户:如果该用户不存在,初始化一个带初始资金的账户
     236            0 :     if (!accountManager_.hasAccount(userId)) {
     237            0 :         LOG() << "[SimulationApp] Initializing new account for user: " << userId;
     238            0 :         accountManager_.createAccount(userId, 1000000.0);  // 默认 100 万
     239              :     }
     240              :     
     241            0 :     engine_.submit(OrderEvent{OrderEventType::SESSION_LOGON, sessionID});
     242            1 : }
     243              : 
     244            1 : void SimulationApp::onLogout(const SessionID& sessionID) {
     245            1 :     LOG() << "[SimulationApp] Session logged out: " << sessionID.to_string();
     246            2 :     engine_.submit(OrderEvent{OrderEventType::SESSION_LOGOUT, sessionID});
     247            1 : }
     248              : 
     249            8 : void SimulationApp::fromApp(const FixMessage& msg, const SessionID& sessionID) {
     250            8 :     const std::string msgType = msg.get_string(tags::MsgType);
     251              :     
     252              :     // =========================================================================
     253              :     // 1. 身份验证:从 Session 提取用户ID(安全路由的核心)
     254              :     // =========================================================================
     255              :     // 关键:不从消息体读取 Account 字段,而是使用 Session 绑定的身份
     256            8 :     std::string userId = extractAccountId(sessionID);
     257              :     
     258            8 :     LOG() << "[SimulationApp] Received MsgType=" << msgType 
     259           16 :           << " from " << sessionID.to_string()
     260            8 :           << " (User: " << userId << ")";
     261              :     
     262              :     // =========================================================================
     263              :     // 2. 消息路由分发
     264              :     // =========================================================================
     265            8 :     if (msgType == "D") {
     266              :         // NewOrderSingle - 新订单
     267            5 :         handleNewOrderSingle(msg, sessionID, userId);
     268              :     } 
     269            3 :     else if (msgType == "F") {
     270              :         // OrderCancelRequest - 撤单请求
     271            1 :         handleOrderCancelRequest(msg, sessionID, userId);
     272              :     }
     273            2 :     else if (msgType == "U1") {
     274              :         // BalanceQueryRequest - 资金查询(自定义)
     275            0 :         handleBalanceQuery(msg, sessionID, userId);
     276              :     }
     277            2 :     else if (msgType == "U3") {
     278              :         // PositionQueryRequest - 持仓查询(自定义)
     279            0 :         handlePositionQuery(msg, sessionID, userId);
     280              :     }
     281            2 :     else if (msgType == "U7") {
     282              :         // InstrumentSearchRequest - 合约搜索(自定义)
     283              :         // 注意:合约搜索不需要用户身份验证
     284            0 :         handleInstrumentSearch(msg, sessionID);
     285              :     }
     286            2 :     else if (msgType == "U9") {
     287              :         // OrderHistoryQueryRequest - 订单历史查询(自定义)
     288            1 :         handleOrderHistoryQuery(msg, sessionID, userId);
     289              :     }
     290              :     else {
     291              :         // 未知消息类型
     292            1 :         LOG() << "[SimulationApp] Unknown message type: " << msgType;
     293            2 :         sendBusinessReject(sessionID, msgType, "Unsupported message type");
     294              :     }
     295            8 : }
     296              : 
     297            1 : void SimulationApp::toApp(FixMessage& msg, const SessionID& sessionID) {
     298            1 :     const std::string msgType = msg.get_string(tags::MsgType);
     299            1 :     LOG() << "[SimulationApp] Sending business message: MsgType=" << msgType
     300            1 :           << " via " << sessionID.to_string();
     301            1 : }
     302              : 
     303            5 : void SimulationApp::onExecutionReport(const SessionID& sessionID, const ExecutionReport& report) {
     304           10 :     LOG() << "[SimulationApp] Sending ExecutionReport to " << sessionID.to_string()
     305            5 :           << " ClOrdID=" << report.clOrdID
     306            5 :           << " OrdStatus=" << static_cast<int>(report.ordStatus);
     307              : 
     308              :     // =========================================================================
     309              :     // 订单/成交持久化(Best effort)
     310              :     // =========================================================================
     311              :     // 说明:
     312              :     // - Order 在 NewOrderSingle 收到时已写入 orders 表(PENDING_NEW);
     313              :     // - 此处根据 ExecutionReport 更新订单状态,并在发生成交时写入 trades 表;
     314              :     // - 持久化失败不应影响撮合/回报链路,因此仅记录日志,不中断处理。
     315            5 :     if (store_ && !report.clOrdID.empty()) {
     316            2 :         Order orderUpdate;
     317            2 :         orderUpdate.clOrdID = report.clOrdID;
     318            2 :         orderUpdate.orderID = report.orderID;
     319            2 :         orderUpdate.cumQty = report.cumQty;
     320            2 :         orderUpdate.leavesQty = report.leavesQty;
     321            2 :         orderUpdate.avgPx = report.avgPx;
     322            2 :         orderUpdate.status = report.ordStatus;
     323              : 
     324            2 :         if (!store_->updateOrder(orderUpdate)) {
     325            0 :             LOG() << "[SimulationApp] Warning: Failed to persist order update: ClOrdID="
     326            0 :                   << report.clOrdID;
     327              :         }
     328              : 
     329              :         // 成交(每次 fill 一条记录)
     330            2 :         if (report.lastShares > 0 && !report.execID.empty()) {
     331            1 :             StoredTrade trade;
     332            1 :             trade.tradeId = report.execID;
     333            1 :             trade.clOrdID = report.clOrdID;
     334            1 :             trade.symbol = report.symbol;
     335            1 :             trade.side = report.side;
     336            1 :             trade.price = report.lastPx;
     337            1 :             trade.quantity = report.lastShares;
     338            1 :             trade.timestamp = to_epoch_millis(report.transactTime);
     339              :             // 模拟撮合为单边撮合:当前系统没有真实的“对手方订单”概念。
     340              :             // 该字段为未来扩展双边撮合/对手方回报预留,因此暂固定为空。
     341            1 :             trade.counterpartyOrderId = "";
     342              : 
     343            1 :             if (!store_->saveTrade(trade)) {
     344            0 :                 LOG() << "[SimulationApp] Warning: Failed to persist trade: ExecID="
     345            0 :                       << report.execID << " ClOrdID=" << report.clOrdID;
     346              :             }
     347            1 :         }
     348            2 :     }
     349              :     
     350              :     // 获取账户ID
     351            5 :     std::string accountId;
     352              :     {
     353            5 :         std::lock_guard<std::mutex> lock(mapMutex_);
     354            5 :         auto it = orderAccountMap_.find(report.clOrdID);
     355            5 :         if (it != orderAccountMap_.end()) {
     356            2 :             accountId = it->second;
     357              :         } else {
     358            3 :             accountId = extractAccountId(sessionID);
     359              :         }
     360            5 :     }
     361              :     
     362              :     // 根据订单状态处理账户和持仓更新
     363            5 :     switch (report.ordStatus) {
     364            1 :         case OrderStatus::FILLED:
     365              :         case OrderStatus::PARTIALLY_FILLED:
     366            1 :             if (report.lastShares > 0) {
     367            1 :                 handleFill(accountId, report);
     368              :             }
     369            1 :             break;
     370            4 :         case OrderStatus::REJECTED:
     371            4 :             handleReject(accountId, report);
     372            4 :             break;
     373            0 :         case OrderStatus::CANCELED:
     374            0 :             handleCancel(accountId, report);
     375            0 :             break;
     376            0 :         default:
     377            0 :             break;
     378              :     }
     379              :     
     380              :     // 清理已完成订单的映射
     381            5 :     if (report.ordStatus == OrderStatus::FILLED ||
     382            4 :         report.ordStatus == OrderStatus::REJECTED ||
     383            0 :         report.ordStatus == OrderStatus::CANCELED) {
     384            5 :         std::lock_guard<std::mutex> lock(mapMutex_);
     385            5 :         orderAccountMap_.erase(report.clOrdID);
     386            5 :         orderMarginInfoMap_.erase(report.clOrdID);
     387            5 :     }
     388              :     
     389              :     // 将 ExecutionReport 转换为 FIX 消息
     390           10 :     FixMessage msg = buildExecutionReport(report);
     391              :     
     392              :     // 通过 SessionManager 发送
     393            5 :     if (!sessionManager_.sendMessage(sessionID, msg)) {
     394           10 :         LOG() << "[SimulationApp] Failed to send ExecutionReport: session not found "
     395            5 :               << sessionID.to_string();
     396              :     }
     397            5 : }
     398              : 
     399            1 : void SimulationApp::handleFill(const std::string& accountId, const ExecutionReport& report) {
     400              :     // 获取合约信息
     401            1 :     const Instrument* instrument = instrumentManager_.getInstrument(report.symbol);
     402            1 :     if (!instrument) {
     403            0 :         LOG() << "[SimulationApp] handleFill: Instrument not found: " << report.symbol;
     404            0 :         return;
     405              :     }
     406              :     
     407              :     // 获取持仓信息
     408            1 :     Position position;
     409            1 :     auto posOpt = positionManager_.getPosition(accountId, report.symbol);
     410            1 :     if (posOpt) {
     411            0 :         position = *posOpt;
     412              :     } else {
     413            1 :         position.accountId = accountId;
     414            1 :         position.instrumentId = report.symbol;
     415              :     }
     416              :     
     417              :     // 计算可平仓数量和开仓数量
     418            1 :     int64_t closeQty = 0;
     419            1 :     int64_t openQty = report.lastShares;
     420              :     
     421            1 :     if (report.side == OrderSide::BUY && position.shortPosition > 0) {
     422              :         // 买单:优先平空仓
     423            0 :         closeQty = std::min(report.lastShares, position.shortPosition);
     424            0 :         openQty = report.lastShares - closeQty;
     425            1 :     } else if (report.side == OrderSide::SELL && position.longPosition > 0) {
     426              :         // 卖单:优先平多仓
     427            0 :         closeQty = std::min(report.lastShares, position.longPosition);
     428            0 :         openQty = report.lastShares - closeQty;
     429              :     }
     430              :     
     431              :     // 1. 先处理平仓部分
     432            1 :     if (closeQty > 0) {
     433            0 :         double closeProfit = positionManager_.closePosition(
     434            0 :             accountId, report.symbol, report.side,
     435            0 :             closeQty, report.lastPx, instrument->volumeMultiple);
     436              :         
     437              :         // 计算释放的保证金
     438            0 :         double releasedMargin = report.lastPx * closeQty * 
     439            0 :                                 instrument->volumeMultiple * instrument->marginRate;
     440              :         
     441              :         // 释放占用保证金
     442            0 :         accountManager_.releaseMargin(accountId, releasedMargin);
     443              :         
     444              :         // 记录平仓盈亏
     445            0 :         accountManager_.addCloseProfit(accountId, closeProfit);
     446              :         
     447            0 :         LOG() << "[SimulationApp] Close position: " << report.symbol
     448            0 :               << " side=" << static_cast<int>(report.side)
     449            0 :               << " qty=" << closeQty
     450            0 :               << " price=" << report.lastPx
     451            0 :               << " profit=" << closeProfit;
     452              :     }
     453              :     
     454              :     // 2. 再处理开仓部分
     455            1 :     if (openQty > 0) {
     456            1 :         double margin = instrument->calculateMargin(report.lastPx, openQty);
     457              :         
     458              :         // 获取冻结的保证金(使用原始总量计算,避免部分成交累计误差)
     459            1 :         double frozenMargin = 0.0;
     460              :         {
     461            1 :             std::lock_guard<std::mutex> lock(mapMutex_);
     462            1 :             auto it = orderMarginInfoMap_.find(report.clOrdID);
     463            1 :             if (it != orderMarginInfoMap_.end()) {
     464              :                 // 使用原始总冻结保证金按比例计算,避免累计误差
     465            1 :                 frozenMargin = it->second.calculateReleaseAmount(openQty);
     466              :             }
     467            1 :         }
     468              :         
     469              :         // 冻结转占用
     470            1 :         accountManager_.confirmMargin(accountId, frozenMargin, margin);
     471              :         
     472              :         // 开仓
     473            1 :         positionManager_.openPosition(
     474            1 :             accountId, report.symbol, report.side,
     475            1 :             openQty, report.lastPx, margin);
     476              :         
     477            2 :         LOG() << "[SimulationApp] Open position: " << report.symbol
     478            2 :               << " side=" << static_cast<int>(report.side)
     479            1 :               << " qty=" << openQty
     480            1 :               << " price=" << report.lastPx
     481            1 :               << " margin=" << margin
     482            1 :               << " frozenReleased=" << frozenMargin;
     483              :     }
     484              : 
     485              :     // 3. 成交后立即刷新该账户的浮动盈亏并推送(reason=2=成交),避免 UI 依赖下一跳行情刷新。
     486              :     // 使用 report.lastPx 作为最新价:当无行情驱动更新时,至少保证“平仓后持仓变为0”的刷新能发生。
     487            1 :     positionManager_.updateProfit(accountId, report.symbol, report.lastPx, instrument->volumeMultiple);
     488            1 :     const double totalProfit = positionManager_.getTotalProfit(accountId);
     489            1 :     accountManager_.updatePositionProfit(accountId, totalProfit);
     490              : 
     491            1 :     pushAccountUpdate(accountId, 2);
     492            1 :     pushPositionUpdate(accountId, report.symbol, 2);
     493            1 : }
     494              : 
     495            4 : void SimulationApp::handleReject(const std::string& accountId, const ExecutionReport& report) {
     496              :     // 释放全部冻结的保证金(拒绝时释放原始总冻结金额)
     497            4 :     double frozenMargin = 0.0;
     498              :     {
     499            4 :         std::lock_guard<std::mutex> lock(mapMutex_);
     500            4 :         auto it = orderMarginInfoMap_.find(report.clOrdID);
     501            4 :         if (it != orderMarginInfoMap_.end()) {
     502              :             // 拒绝时释放全部原始冻结保证金
     503            1 :             frozenMargin = it->second.originalFrozenMargin;
     504              :         }
     505            4 :     }
     506              :     
     507            4 :     if (frozenMargin > 0) {
     508            1 :         accountManager_.unfreezeMargin(accountId, frozenMargin);
     509            1 :         LOG() << "[SimulationApp] Released frozen margin on reject: " << frozenMargin;
     510              :     }
     511            4 : }
     512              : 
     513            0 : void SimulationApp::handleCancel(const std::string& accountId, const ExecutionReport& report) {
     514              :     // 释放剩余未释放的冻结保证金(撤单时只释放未成交部分)
     515            0 :     double frozenMargin = 0.0;
     516              :     {
     517            0 :         std::lock_guard<std::mutex> lock(mapMutex_);
     518            0 :         auto it = orderMarginInfoMap_.find(report.clOrdID);
     519            0 :         if (it != orderMarginInfoMap_.end()) {
     520              :             // 撤单时释放剩余未释放的冻结保证金
     521            0 :             frozenMargin = it->second.getRemainingFrozen();
     522              :         }
     523            0 :     }
     524              :     
     525            0 :     if (frozenMargin > 0) {
     526            0 :         accountManager_.unfreezeMargin(accountId, frozenMargin);
     527            0 :         LOG() << "[SimulationApp] Released frozen margin on cancel: " << frozenMargin;
     528              :     }
     529            0 : }
     530              : 
     531           12 : std::string SimulationApp::extractAccountId(const SessionID& sessionID) const {
     532              :     // 通过 SessionManager 获取 Session 对象,提取真实的客户端标识
     533           12 :     auto session = sessionManager_.findSession(sessionID);
     534           12 :     if (session) {
     535            4 :         const std::string& clientId = session->get_client_comp_id();
     536            4 :         if (!clientId.empty()) {
     537            4 :             return clientId;
     538              :         }
     539              :     }
     540              :     // 安全策略:无法获取有效的 clientCompID 时返回空字符串
     541              :     // 调用方应检查返回值并拒绝处理
     542           16 :     LOG() << "[SimulationApp] Error: Could not extract clientCompID for session "
     543            8 :           << sessionID.to_string();
     544           16 :     return "";
     545           12 : }
     546              : 
     547           11 : Account SimulationApp::getOrCreateAccount(const std::string& accountId, double initialBalance) {
     548           11 :     auto accountOpt = accountManager_.getAccount(accountId);
     549           11 :     if (accountOpt) {
     550            1 :         return *accountOpt;
     551              :     }
     552           10 :     return accountManager_.createAccount(accountId, initialBalance);
     553           11 : }
     554              : 
     555              : // ============================================================================
     556              : // 消息处理函数实现
     557              : // ============================================================================
     558              : 
     559            5 : void SimulationApp::handleNewOrderSingle(const FixMessage& msg, const SessionID& sessionID, const std::string& userId) {
     560              :     // 解析订单
     561            5 :     ParseResult result = parseNewOrderSingle(msg, sessionID);
     562            5 :     if (!result.success) {
     563            0 :         LOG() << "[SimulationApp] Parse failed: " << result.error;
     564            0 :         ExecutionReport reject;
     565            0 :         reject.clOrdID = msg.has(tags::ClOrdID) ? msg.get_string(tags::ClOrdID) : "";
     566            0 :         reject.symbol = msg.has(tags::Symbol) ? msg.get_string(tags::Symbol) : "";
     567            0 :         reject.ordStatus = OrderStatus::REJECTED;
     568            0 :         reject.execTransType = ExecTransType::NEW;
     569            0 :         reject.ordRejReason = 99;
     570            0 :         reject.text = result.error;
     571            0 :         reject.transactTime = std::chrono::system_clock::now();
     572            0 :         onExecutionReport(sessionID, reject);
     573            0 :         return;
     574            0 :     }
     575              :     
     576            5 :     Order& order = result.order;
     577              : 
     578              :     // =========================================================================
     579              :     // 订单持久化(Best effort)
     580              :     // =========================================================================
     581              :     // 将订单的初始状态写入存储(通常为 PENDING_NEW)。
     582              :     // 后续状态变化由 onExecutionReport() 驱动 updateOrder() 完成。
     583            5 :     if (store_) {
     584            2 :         if (!store_->saveOrderForAccount(order, userId)) {
     585            0 :             LOG() << "[SimulationApp] Warning: Failed to persist new order: ClOrdID="
     586            0 :                   << order.clOrdID;
     587              :         }
     588              :     }
     589              :     
     590              :     // 关键:使用从 Session 提取的 userId,而非消息体中的 Account 字段
     591              :     // 这是安全路由的核心 - 防止用户伪造账户ID
     592              :     
     593              :     // 确保账户存在
     594            5 :     getOrCreateAccount(userId);
     595              :     
     596              :     // 获取合约信息
     597            5 :     const Instrument* instrument = instrumentManager_.getInstrument(order.symbol);
     598            5 :     if (!instrument) {
     599            2 :         LOG() << "[SimulationApp] Instrument not found: " << order.symbol;
     600            2 :         ExecutionReport reject;
     601            2 :         reject.clOrdID = order.clOrdID;
     602            2 :         reject.symbol = order.symbol;
     603            2 :         reject.side = order.side;
     604            2 :         reject.ordType = order.ordType;
     605            2 :         reject.orderQty = order.orderQty;
     606            2 :         reject.price = order.price;
     607            2 :         reject.ordStatus = OrderStatus::REJECTED;
     608            2 :         reject.execTransType = ExecTransType::NEW;
     609            2 :         reject.ordRejReason = static_cast<int>(RejectReason::INSTRUMENT_NOT_FOUND);
     610            2 :         reject.text = "Instrument not found: " + order.symbol;
     611            2 :         reject.transactTime = std::chrono::system_clock::now();
     612            2 :         onExecutionReport(sessionID, reject);
     613            2 :         return;
     614            2 :     }
     615              :     
     616              :     // 获取账户和持仓信息
     617            3 :     auto accountOpt = accountManager_.getAccount(userId);
     618            3 :     if (!accountOpt) {
     619            0 :         LOG() << "[SimulationApp] Account not found: " << userId;
     620            0 :         return;
     621              :     }
     622              :     
     623            3 :     Position position;
     624            3 :     auto posOpt = positionManager_.getPosition(userId, order.symbol);
     625            3 :     if (posOpt) {
     626            0 :         position = *posOpt;
     627              :     } else {
     628            3 :         position.accountId = userId;
     629            3 :         position.instrumentId = order.symbol;
     630              :     }
     631              :     
     632              :     // 获取行情快照
     633            3 :     MarketDataSnapshot snapshot;
     634            3 :     const MarketDataSnapshot* snapshotPtr = engine_.getMarketSnapshot(order.symbol);
     635            3 :     if (snapshotPtr) {
     636            2 :         snapshot = *snapshotPtr;
     637              :     } else {
     638            1 :         snapshot.instrumentId = order.symbol;
     639            1 :         snapshot.upperLimitPrice = instrument->upperLimitPrice;
     640            1 :         snapshot.lowerLimitPrice = instrument->lowerLimitPrice;
     641              :     }
     642              :     
     643              :     // 判断开平标志
     644            3 :     OffsetFlag offsetFlag = OffsetFlag::OPEN;
     645            3 :     if (order.side == OrderSide::BUY && position.shortPosition > 0) {
     646            0 :         offsetFlag = OffsetFlag::CLOSE;
     647            3 :     } else if (order.side == OrderSide::SELL && position.longPosition > 0) {
     648            0 :         offsetFlag = OffsetFlag::CLOSE;
     649              :     }
     650              :     
     651              :     // 风控检查
     652              :     CheckResult checkResult = riskManager_.checkOrder(
     653            3 :         order, *accountOpt, position, *instrument, snapshot, offsetFlag);
     654              :     
     655            3 :     if (!checkResult.passed) {
     656            1 :         LOG() << "[SimulationApp] Risk check failed: " << checkResult.rejectText;
     657            1 :         ExecutionReport reject;
     658            1 :         reject.clOrdID = order.clOrdID;
     659            1 :         reject.symbol = order.symbol;
     660            1 :         reject.side = order.side;
     661            1 :         reject.ordType = order.ordType;
     662            1 :         reject.orderQty = order.orderQty;
     663            1 :         reject.price = order.price;
     664            1 :         reject.ordStatus = OrderStatus::REJECTED;
     665            1 :         reject.execTransType = ExecTransType::NEW;
     666            1 :         reject.ordRejReason = static_cast<int>(checkResult.rejectReason);
     667            1 :         reject.text = checkResult.rejectText;
     668            1 :         reject.transactTime = std::chrono::system_clock::now();
     669            1 :         onExecutionReport(sessionID, reject);
     670            1 :         return;
     671            1 :     }
     672              :     
     673              :     // 开仓订单:冻结保证金
     674            2 :     if (offsetFlag == OffsetFlag::OPEN) {
     675            2 :         double requiredMargin = riskManager_.calculateRequiredMargin(order, *instrument);
     676            2 :         if (!accountManager_.freezeMargin(userId, requiredMargin)) {
     677            0 :             LOG() << "[SimulationApp] Failed to freeze margin: " << requiredMargin;
     678            0 :             ExecutionReport reject;
     679            0 :             reject.clOrdID = order.clOrdID;
     680            0 :             reject.symbol = order.symbol;
     681            0 :             reject.side = order.side;
     682            0 :             reject.ordType = order.ordType;
     683            0 :             reject.orderQty = order.orderQty;
     684            0 :             reject.price = order.price;
     685            0 :             reject.ordStatus = OrderStatus::REJECTED;
     686            0 :             reject.execTransType = ExecTransType::NEW;
     687            0 :             reject.ordRejReason = static_cast<int>(RejectReason::INSUFFICIENT_FUNDS);
     688            0 :             reject.text = "Failed to freeze margin";
     689            0 :             reject.transactTime = std::chrono::system_clock::now();
     690            0 :             onExecutionReport(sessionID, reject);
     691            0 :             return;
     692            0 :         }
     693              :         
     694              :         {
     695            2 :             std::lock_guard<std::mutex> lock(mapMutex_);
     696            2 :             orderMarginInfoMap_[order.clOrdID] = OrderMarginInfo(requiredMargin, order.orderQty);
     697            2 :         }
     698              :     }
     699              :     
     700              :     // 记录订单到账户的映射
     701              :     {
     702            2 :         std::lock_guard<std::mutex> lock(mapMutex_);
     703            2 :         orderAccountMap_[order.clOrdID] = userId;
     704            2 :     }
     705              :     
     706              :     // 提交到撮合引擎(传入真实的用户ID)
     707            2 :     engine_.submit(OrderEvent::newOrder(order, userId));
     708           10 : }
     709              : 
     710            1 : void SimulationApp::handleOrderCancelRequest(const FixMessage& msg, const SessionID& sessionID, const std::string& userId) {
     711            1 :     CancelRequest req = parseCancelRequest(msg, sessionID);
     712              :     
     713              :     // 安全检查:验证撤单请求是否属于当前用户
     714              :     {
     715            1 :         std::lock_guard<std::mutex> lock(mapMutex_);
     716            1 :         auto it = orderAccountMap_.find(req.origClOrdID);
     717            1 :         if (it == orderAccountMap_.end()) {
     718            1 :             LOG() << "[SimulationApp] Cancel rejected: order " << req.origClOrdID << " not found";
     719            3 :             sendBusinessReject(sessionID, "F", "Order not found: " + req.origClOrdID);
     720            1 :             return;
     721              :         }
     722            0 :         if (it->second != userId) {
     723            0 :             LOG() << "[SimulationApp] Cancel rejected: order " << req.origClOrdID 
     724            0 :                   << " belongs to " << it->second << ", not " << userId;
     725            0 :             sendBusinessReject(sessionID, "F", "Not authorized to cancel this order");
     726            0 :             return;
     727              :         }
     728            1 :     }
     729              :     
     730            0 :     engine_.submit(OrderEvent::cancelRequest(req, userId));
     731            1 : }
     732              : 
     733            0 : void SimulationApp::handleBalanceQuery(const FixMessage& msg, const SessionID& sessionID, const std::string& userId) {
     734            0 :     LOG() << "[SimulationApp] Processing balance query for user: " << userId;
     735              :     
     736              :     // 查询账户数据
     737            0 :     auto accountOpt = accountManager_.getAccount(userId);
     738            0 :     if (!accountOpt) {
     739            0 :         LOG() << "[SimulationApp] Account not found for balance query: " << userId;
     740            0 :         sendBusinessReject(sessionID, "U1", "Account not found");
     741            0 :         return;
     742              :     }
     743              :     
     744            0 :     const Account& account = *accountOpt;
     745              :     
     746              :     // 构造 U2 响应消息 (BalanceQueryResponse)
     747            0 :     FixMessage response;
     748            0 :     response.set(tags::MsgType, "U2");
     749              :     
     750              :     // 回填请求ID(如果客户端发了的话)
     751            0 :     if (msg.has(tags::RequestID)) {
     752            0 :         response.set(tags::RequestID, msg.get_string(tags::RequestID));
     753              :     }
     754              :     
     755              :     // 账户标识
     756            0 :     response.set(tags::Account, userId);
     757              :     
     758              :     // 资金信息(使用自定义 Tag)
     759              :     // 注意:FixMessage::set 只支持 string 和 int,需要将 double 转为 string
     760            0 :     std::ostringstream oss;
     761            0 :     oss << std::fixed << std::setprecision(2);
     762              :     
     763            0 :     oss.str(""); oss << account.balance;
     764            0 :     response.set(tags::Balance, oss.str());
     765              :     
     766            0 :     oss.str(""); oss << account.available;
     767            0 :     response.set(tags::Available, oss.str());
     768              :     
     769            0 :     oss.str(""); oss << account.frozenMargin;
     770            0 :     response.set(tags::FrozenMargin, oss.str());
     771              :     
     772            0 :     oss.str(""); oss << account.usedMargin;
     773            0 :     response.set(tags::UsedMargin, oss.str());
     774              :     
     775            0 :     oss.str(""); oss << account.positionProfit;
     776            0 :     response.set(tags::PositionProfit, oss.str());
     777              :     
     778            0 :     oss.str(""); oss << account.closeProfit;
     779            0 :     response.set(tags::CloseProfit, oss.str());
     780              :     
     781            0 :     oss.str(""); oss << account.getDynamicEquity();
     782            0 :     response.set(tags::DynamicEquity, oss.str());
     783              :     
     784            0 :     oss.str(""); oss << account.getRiskRatio();
     785            0 :     response.set(tags::RiskRatio, oss.str());
     786              :     
     787              :     // 发送响应
     788            0 :     if (!sessionManager_.sendMessage(sessionID, response)) {
     789            0 :         LOG() << "[SimulationApp] Failed to send balance response to " << sessionID.to_string();
     790              :     } else {
     791            0 :         LOG() << "[SimulationApp] Sent balance response to " << userId
     792            0 :               << " Balance=" << account.balance
     793            0 :               << " Available=" << account.available;
     794              :     }
     795            0 : }
     796              : 
     797            0 : void SimulationApp::handlePositionQuery(const FixMessage& msg, const SessionID& sessionID, const std::string& userId) {
     798            0 :     LOG() << "[SimulationApp] Processing position query for user: " << userId;
     799              :     
     800              :     // 获取用户的所有持仓
     801            0 :     auto positions = positionManager_.getPositionsByAccount(userId);
     802              :     
     803              :     // 构造 U4 响应消息 (PositionQueryResponse)
     804            0 :     FixMessage response;
     805            0 :     response.set(tags::MsgType, "U4");
     806              :     
     807              :     // 回填请求ID
     808            0 :     if (msg.has(tags::RequestID)) {
     809            0 :         response.set(tags::RequestID, msg.get_string(tags::RequestID));
     810              :     }
     811              :     
     812            0 :     response.set(tags::Account, userId);
     813            0 :     response.set(tags::NoPositions, static_cast<int>(positions.size()));
     814              :     
     815              :     // 注意:FIX 协议的 Repeating Group 处理比较复杂
     816              :     // 这里简化处理:将持仓信息序列化为文本格式放在 Text 字段
     817              :     // 实际生产环境应该使用标准的 Repeating Group 格式
     818            0 :     if (!positions.empty()) {
     819            0 :         std::ostringstream posText;
     820            0 :         posText << std::fixed << std::setprecision(2);
     821            0 :         for (const auto& pos : positions) {
     822            0 :             posText << pos.instrumentId << ":"
     823            0 :                     << "L" << pos.longPosition << "@" << pos.longAvgPrice << ","
     824            0 :                     << "S" << pos.shortPosition << "@" << pos.shortAvgPrice << ";";
     825              :         }
     826            0 :         response.set(tags::Text, posText.str());
     827            0 :     }
     828              :     
     829              :     // 发送响应
     830            0 :     if (!sessionManager_.sendMessage(sessionID, response)) {
     831            0 :         LOG() << "[SimulationApp] Failed to send position response to " << sessionID.to_string();
     832              :     } else {
     833            0 :         LOG() << "[SimulationApp] Sent position response to " << userId
     834            0 :               << " with " << positions.size() << " positions";
     835              :     }
     836            0 : }
     837              : 
     838            2 : void SimulationApp::sendBusinessReject(const SessionID& sessionID, const std::string& refMsgType, const std::string& reason) {
     839              :     // 构造 BusinessMessageReject (MsgType = j)
     840            2 :     FixMessage reject;
     841            2 :     reject.set(tags::MsgType, "j");
     842            2 :     reject.set(tags::Text, reason);
     843              :     // RefMsgType 标准 Tag 是 372,这里简化处理放在 Text 中
     844              :     
     845            4 :     LOG() << "[SimulationApp] Sending BusinessReject for MsgType=" << refMsgType
     846            2 :           << " reason: " << reason;
     847              :     
     848            2 :     if (!sessionManager_.sendMessage(sessionID, reject)) {
     849            2 :         LOG() << "[SimulationApp] Failed to send BusinessReject to " << sessionID.to_string();
     850              :     }
     851            2 : }
     852              : 
     853              : // ============================================================================
     854              : // 行情驱动账户更新实现
     855              : // ============================================================================
     856              : 
     857            2 : void SimulationApp::onMarketDataUpdate(const std::string& instrumentId, double lastPrice) {
     858              :     // 获取合约信息
     859            2 :     const Instrument* instrument = instrumentManager_.getInstrument(instrumentId);
     860            2 :     if (!instrument) {
     861            0 :         return;
     862              :     }
     863              :     
     864              :     // 获取该合约的所有持仓
     865            2 :     auto allPositions = positionManager_.getAllPositions();
     866              :     
     867              :     // 收集受影响的账户
     868            2 :     std::set<std::string> affectedAccounts;
     869              :     
     870            2 :     for (const auto& pos : allPositions) {
     871            0 :         if (pos.instrumentId == instrumentId && pos.hasPosition()) {
     872              :             // 更新持仓浮动盈亏
     873            0 :             double newProfit = positionManager_.updateProfit(
     874            0 :                 pos.accountId, instrumentId, lastPrice, instrument->volumeMultiple);
     875              :             
     876            0 :             affectedAccounts.insert(pos.accountId);
     877              :             
     878            0 :             LOG() << "[SimulationApp] Updated position profit for " << pos.accountId
     879            0 :                   << " " << instrumentId << " profit=" << newProfit;
     880              :         }
     881              :     }
     882              :     
     883              :     // 更新受影响账户的持仓盈亏总额,并推送给 Client
     884            2 :     for (const auto& accountId : affectedAccounts) {
     885            0 :         double totalProfit = positionManager_.getTotalProfit(accountId);
     886            0 :         accountManager_.updatePositionProfit(accountId, totalProfit);
     887              :         
     888              :         // 推送账户更新给 Client(行情变化触发)
     889            0 :         pushAccountUpdate(accountId, 1);  // 1 = 行情变化
     890              :         
     891              :         // 同时推送持仓更新(包含最新盈亏)
     892            0 :         pushPositionUpdate(accountId, instrumentId, 1);  // 1 = 行情变化
     893              :     }
     894            2 : }
     895              : 
     896            2 : void SimulationApp::pushAccountUpdate(const std::string& userId, int reason) {
     897              :     // 查找用户对应的 Session
     898            2 :     auto sessionOpt = findSessionByUserId(userId);
     899            2 :     if (!sessionOpt) {
     900            0 :         return;
     901              :     }
     902              :     
     903              :     // 获取账户数据
     904            2 :     auto accountOpt = accountManager_.getAccount(userId);
     905            2 :     if (!accountOpt) {
     906            0 :         return;
     907              :     }
     908              :     
     909            2 :     const Account& account = *accountOpt;
     910              :     
     911              :     // 构造 U5 推送消息 (AccountUpdateNotification)
     912            2 :     FixMessage msg;
     913            2 :     msg.set(tags::MsgType, "U5");
     914            2 :     msg.set(tags::Account, userId);
     915            2 :     msg.set(tags::UpdateType, 1);  // 1 = 账户更新
     916            2 :     msg.set(tags::UpdateReason, reason);
     917              :     
     918            2 :     std::ostringstream oss;
     919            2 :     oss << std::fixed << std::setprecision(2);
     920              :     
     921            4 :     oss.str(""); oss << account.balance;
     922            2 :     msg.set(tags::Balance, oss.str());
     923              :     
     924            4 :     oss.str(""); oss << account.available;
     925            2 :     msg.set(tags::Available, oss.str());
     926              :     
     927            4 :     oss.str(""); oss << account.frozenMargin;
     928            2 :     msg.set(tags::FrozenMargin, oss.str());
     929              :     
     930            4 :     oss.str(""); oss << account.usedMargin;
     931            2 :     msg.set(tags::UsedMargin, oss.str());
     932              :     
     933            4 :     oss.str(""); oss << account.positionProfit;
     934            2 :     msg.set(tags::PositionProfit, oss.str());
     935              : 
     936            4 :     oss.str(""); oss << account.closeProfit;
     937            2 :     msg.set(tags::CloseProfit, oss.str());
     938              : 
     939            4 :     oss.str(""); oss << account.getDynamicEquity();
     940            2 :     msg.set(tags::DynamicEquity, oss.str());
     941              : 
     942            4 :     oss.str(""); oss << account.getRiskRatio();
     943            2 :     msg.set(tags::RiskRatio, oss.str());
     944              : 
     945              :     // 发送推送
     946            2 :     if (!sessionManager_.sendMessage(*sessionOpt, msg)) {
     947              :         // 用户可能已离线/会话已不可用:推送属于“尽力而为”,失败时静默丢弃即可。
     948              :     }
     949            2 : }
     950              : 
     951            1 : void SimulationApp::pushPositionUpdate(const std::string& userId, const std::string& instrumentId, int reason) {
     952              :     // 查找用户对应的 Session
     953            1 :     auto sessionOpt = findSessionByUserId(userId);
     954            1 :     if (!sessionOpt) {
     955            0 :         return;
     956              :     }
     957              :     
     958              :     // 获取持仓数据
     959            1 :     auto posOpt = positionManager_.getPosition(userId, instrumentId);
     960            1 :     if (!posOpt) {
     961            0 :         return;
     962              :     }
     963              :     
     964            1 :     const Position& pos = *posOpt;
     965              :     
     966              :     // 构造 U6 推送消息 (PositionUpdateNotification)
     967            1 :     FixMessage msg;
     968            1 :     msg.set(tags::MsgType, "U6");
     969            1 :     msg.set(tags::Account, userId);
     970            1 :     msg.set(tags::UpdateType, 2);  // 2 = 持仓更新
     971            1 :     msg.set(tags::UpdateReason, reason);
     972            1 :     msg.set(tags::InstrumentID, instrumentId);
     973              :     
     974            1 :     std::ostringstream oss;
     975            1 :     oss << std::fixed << std::setprecision(2);
     976              :     
     977            1 :     msg.set(tags::LongPosition, static_cast<int>(pos.longPosition));
     978              :     
     979            2 :     oss.str(""); oss << pos.longAvgPrice;
     980            1 :     msg.set(tags::LongAvgPrice, oss.str());
     981              :     
     982            1 :     msg.set(tags::ShortPosition, static_cast<int>(pos.shortPosition));
     983              :     
     984            2 :     oss.str(""); oss << pos.shortAvgPrice;
     985            1 :     msg.set(tags::ShortAvgPrice, oss.str());
     986              :     
     987            2 :     oss.str(""); oss << pos.getTotalProfit();
     988            1 :     msg.set(tags::PositionProfit, oss.str());
     989              :     
     990              :     // 发送推送
     991            1 :     if (!sessionManager_.sendMessage(*sessionOpt, msg)) {
     992              :         // 用户可能已离线/会话已不可用:推送属于“尽力而为”,失败时静默丢弃即可。
     993              :     }
     994            1 : }
     995              : 
     996            3 : std::optional<SessionID> SimulationApp::findSessionByUserId(const std::string& userId) const {
     997              :     // 遍历所有 Session,找到 clientCompID == userId 的那个
     998            3 :     std::optional<SessionID> result;
     999              :     
    1000            3 :     sessionManager_.forEachSession([&](const SessionID& sid, std::shared_ptr<Session> session) {
    1001              :         // 已找到则跳过后续遍历
    1002            3 :         if (result.has_value()) {
    1003            0 :             return;
    1004              :         }
    1005              :         // 使用 Session::get_client_comp_id() 获取真实的客户端标识
    1006            3 :         if (session && session->get_client_comp_id() == userId) {
    1007            3 :             result = sid;
    1008              :         }
    1009              :     });
    1010              :     
    1011            3 :     return result;
    1012            0 : }
    1013              : 
    1014              : // ============================================================================
    1015              : // 合约搜索实现
    1016              : // ============================================================================
    1017              : 
    1018            0 : void SimulationApp::handleInstrumentSearch(const FixMessage& msg, const SessionID& sessionID) {
    1019              :     // 获取搜索参数
    1020            0 :     std::string pattern;
    1021            0 :     if (msg.has(tags::SearchPattern)) {
    1022            0 :         pattern = msg.get_string(tags::SearchPattern);
    1023              :     }
    1024              :     
    1025            0 :     size_t maxResults = 10;  // 默认返回 10 条
    1026            0 :     if (msg.has(tags::MaxResults)) {
    1027            0 :         maxResults = static_cast<size_t>(msg.get_int(tags::MaxResults));
    1028            0 :         if (maxResults > 50) maxResults = 50;  // 限制最大返回数量
    1029              :     }
    1030              :     
    1031            0 :     LOG() << "[SimulationApp] Processing instrument search: pattern=" << pattern
    1032            0 :           << " maxResults=" << maxResults;
    1033              :     
    1034              :     // 搜索合约
    1035            0 :     auto results = instrumentManager_.searchByPrefix(pattern, maxResults);
    1036              :     
    1037              :     // 构造 U8 响应消息 (InstrumentSearchResponse)
    1038            0 :     FixMessage response;
    1039            0 :     response.set(tags::MsgType, "U8");
    1040              :     
    1041              :     // 回填请求ID
    1042            0 :     if (msg.has(tags::RequestID)) {
    1043            0 :         response.set(tags::RequestID, msg.get_string(tags::RequestID));
    1044              :     }
    1045              :     
    1046            0 :     response.set(tags::SearchPattern, pattern);
    1047            0 :     response.set(tags::ResultCount, static_cast<int>(results.size()));
    1048              :     
    1049              :     // 将合约列表序列化为逗号分隔的字符串
    1050            0 :     if (!results.empty()) {
    1051            0 :         std::ostringstream oss;
    1052            0 :         for (size_t i = 0; i < results.size(); ++i) {
    1053            0 :             if (i > 0) oss << ",";
    1054            0 :             oss << results[i];
    1055              :         }
    1056            0 :         response.set(tags::InstrumentList, oss.str());
    1057            0 :     }
    1058              :     
    1059              :     // 发送响应
    1060            0 :     if (!sessionManager_.sendMessage(sessionID, response)) {
    1061            0 :         LOG() << "[SimulationApp] Failed to send instrument search response to "
    1062            0 :               << sessionID.to_string();
    1063              :     } else {
    1064            0 :         LOG() << "[SimulationApp] Sent instrument search response: " << results.size() << " results";
    1065              :     }
    1066            0 : }
    1067              : 
    1068            1 : void SimulationApp::handleOrderHistoryQuery(const FixMessage& msg, const SessionID& sessionID, const std::string& userId) {
    1069            1 :     LOG() << "[SimulationApp] Processing order history query for user: " << userId;
    1070              : 
    1071            1 :     if (!store_) {
    1072              :         // 未启用持久化时,返回空列表(best effort)
    1073            0 :         FixMessage response;
    1074            0 :         response.set(tags::MsgType, "U10");
    1075            0 :         if (msg.has(tags::RequestID)) {
    1076            0 :             response.set(tags::RequestID, msg.get_string(tags::RequestID));
    1077              :         }
    1078            0 :         response.set(tags::Text, "");
    1079            0 :         sessionManager_.sendMessage(sessionID, response);
    1080            0 :         return;
    1081            0 :     }
    1082              : 
    1083            1 :     std::vector<Order> orders = store_->loadOrdersByAccount(userId);
    1084            1 :     if (msg.has(tags::Symbol) && !msg.get_string(tags::Symbol).empty()) {
    1085            0 :         const std::string symbol = msg.get_string(tags::Symbol);
    1086            0 :         orders.erase(
    1087            0 :             std::remove_if(
    1088              :                 orders.begin(),
    1089              :                 orders.end(),
    1090            0 :                 [&](const Order& o) { return o.symbol != symbol; }),
    1091            0 :             orders.end());
    1092            0 :     }
    1093              : 
    1094            1 :     auto toClientOrderState = [](OrderStatus status) -> int {
    1095              :         // client::OrderState: PENDING_NEW=0, NEW=1, PARTIALLY_FILLED=2, FILLED=3, CANCELED=4, REJECTED=5
    1096            1 :         switch (status) {
    1097            0 :             case OrderStatus::PENDING_NEW: return 0;
    1098            0 :             case OrderStatus::NEW: return 1;
    1099            1 :             case OrderStatus::PARTIALLY_FILLED: return 2;
    1100            0 :             case OrderStatus::FILLED: return 3;
    1101            0 :             case OrderStatus::CANCELED: return 4;
    1102            0 :             case OrderStatus::REJECTED: return 5;
    1103            0 :             default: return 0;
    1104              :         }
    1105              :     };
    1106              : 
    1107            1 :     auto toSideString = [](OrderSide side) -> const char* {
    1108            1 :         return side == OrderSide::BUY ? "BUY" : "SELL";
    1109              :     };
    1110              : 
    1111              :     // 序列化格式与 client 本地文件保存格式对齐:每行一个订单,字段用 '|' 分隔。
    1112              :     // clOrdID|orderId|symbol|side|price|orderQty|filledQty|avgPx|state|text|updateTime
    1113            1 :     std::ostringstream text;
    1114            1 :     text << std::fixed << std::setprecision(6);
    1115            2 :     for (const auto& o : orders) {
    1116            1 :         text << o.clOrdID << "|"
    1117            1 :              << o.orderID << "|"
    1118            1 :              << o.symbol << "|"
    1119            1 :              << toSideString(o.side) << "|"
    1120            1 :              << o.price << "|"
    1121            1 :              << o.orderQty << "|"
    1122            1 :              << o.cumQty << "|"
    1123            1 :              << o.avgPx << "|"
    1124            1 :              << toClientOrderState(o.status) << "|"
    1125              :              << "-" << "|"
    1126              :              << "-"
    1127            1 :              << "\n";
    1128              :     }
    1129              : 
    1130            1 :     FixMessage response;
    1131            1 :     response.set(tags::MsgType, "U10");
    1132            1 :     if (msg.has(tags::RequestID)) {
    1133            1 :         response.set(tags::RequestID, msg.get_string(tags::RequestID));
    1134              :     }
    1135            1 :     response.set(tags::Account, userId);
    1136            1 :     response.set(tags::Text, text.str());
    1137              : 
    1138            1 :     if (!sessionManager_.sendMessage(sessionID, response)) {
    1139            0 :         LOG() << "[SimulationApp] Failed to send order history response to " << sessionID.to_string();
    1140              :     }
    1141            1 : }
    1142              : 
    1143              : } // namespace fix40
        

Generated by: LCOV version 2.0-1