LCOV - code coverage report
Current view: top level - src/market - mock_md_adapter.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 167 167
Test Date: 2025-12-19 03:13:09 Functions: 93.8 % 16 15

            Line data    Source code
       1              : /**
       2              :  * @file mock_md_adapter.cpp
       3              :  * @brief 模拟行情适配器实现
       4              :  */
       5              : 
       6              : #include "market/mock_md_adapter.hpp"
       7              : #include "base/logger.hpp"
       8              : #include <iomanip>
       9              : #include <sstream>
      10              : #include <ctime>
      11              : 
      12              : namespace fix40 {
      13              : 
      14              : namespace {
      15              : // 线程安全的 localtime 封装(仅支持 POSIX 系统)
      16           47 : std::tm safe_localtime(std::time_t time) {
      17           47 :     std::tm tm{};
      18           47 :     localtime_r(&time, &tm);
      19           47 :     return tm;
      20              : }
      21              : } // anonymous namespace
      22              : 
      23           14 : MockMdAdapter::MockMdAdapter(moodycamel::BlockingConcurrentQueue<MarketData>& queue)
      24              :     : MdAdapter(queue)
      25           14 :     , rng_(std::random_device{}())
      26           14 :     , tradingDay_([]() {
      27              :         // 生成模拟交易日(当前日期)
      28           14 :         auto now = std::chrono::system_clock::now();
      29           14 :         auto time = std::chrono::system_clock::to_time_t(now);
      30           14 :         std::tm tm = safe_localtime(time);
      31           14 :         std::ostringstream oss;
      32           14 :         oss << std::put_time(&tm, "%Y%m%d");
      33           28 :         return oss.str();
      34           28 :     }())
      35              : {
      36           14 : }
      37              : 
      38           28 : MockMdAdapter::~MockMdAdapter() {
      39           14 :     stop();
      40           14 : }
      41              : 
      42           13 : bool MockMdAdapter::start() {
      43           13 :     if (running_.load()) {
      44            1 :         return true;
      45              :     }
      46              : 
      47           12 :     notifyState(MdAdapterState::CONNECTING, "Connecting to mock data source...");
      48              :     
      49           12 :     running_.store(true);
      50           12 :     state_.store(MdAdapterState::READY);
      51              :     
      52           12 :     workerThread_ = std::thread(&MockMdAdapter::run, this);
      53              :     
      54           12 :     notifyState(MdAdapterState::READY, "Mock adapter ready");
      55           12 :     LOG() << "[MockMdAdapter] Started, trading day: " << tradingDay_;
      56              :     
      57           12 :     return true;
      58              : }
      59              : 
      60           27 : void MockMdAdapter::stop() {
      61           27 :     if (!running_.load()) {
      62           15 :         return;
      63              :     }
      64              : 
      65           12 :     running_.store(false);
      66              :     
      67           12 :     if (workerThread_.joinable()) {
      68           12 :         workerThread_.join();
      69              :     }
      70              : 
      71           12 :     state_.store(MdAdapterState::DISCONNECTED);
      72           12 :     notifyState(MdAdapterState::DISCONNECTED, "Mock adapter stopped");
      73           12 :     LOG() << "[MockMdAdapter] Stopped";
      74              : }
      75              : 
      76            8 : bool MockMdAdapter::subscribe(const std::vector<std::string>& instruments) {
      77            8 :     if (state_.load() != MdAdapterState::READY) {
      78            1 :         LOG() << "[MockMdAdapter] Cannot subscribe: not ready";
      79            1 :         return false;
      80              :     }
      81              : 
      82            7 :     std::lock_guard<std::mutex> lock(mutex_);
      83           18 :     for (const auto& inst : instruments) {
      84           11 :         subscribedInstruments_.insert(inst);
      85              :         // 如果没有设置基准价,使用默认值
      86           11 :         if (basePrices_.find(inst) == basePrices_.end()) {
      87           10 :             basePrices_[inst] = 5000.0;
      88              :         }
      89           11 :         if (lastPrices_.find(inst) == lastPrices_.end()) {
      90           10 :             lastPrices_[inst] = basePrices_[inst];
      91              :         }
      92           11 :         LOG() << "[MockMdAdapter] Subscribed: " << inst;
      93              :     }
      94              :     
      95            7 :     return true;
      96            7 : }
      97              : 
      98            1 : bool MockMdAdapter::unsubscribe(const std::vector<std::string>& instruments) {
      99            1 :     std::lock_guard<std::mutex> lock(mutex_);
     100            2 :     for (const auto& inst : instruments) {
     101            1 :         subscribedInstruments_.erase(inst);
     102            1 :         LOG() << "[MockMdAdapter] Unsubscribed: " << inst;
     103              :     }
     104            1 :     return true;
     105            1 : }
     106              : 
     107            1 : void MockMdAdapter::setStateCallback(StateCallback callback) {
     108            1 :     std::lock_guard<std::mutex> lock(mutex_);
     109            1 :     stateCallback_ = std::move(callback);
     110            1 : }
     111              : 
     112            1 : std::string MockMdAdapter::getTradingDay() const {
     113            1 :     return tradingDay_;
     114              : }
     115              : 
     116            1 : void MockMdAdapter::setBasePrice(const std::string& instrument, double basePrice) {
     117            1 :     std::lock_guard<std::mutex> lock(mutex_);
     118            1 :     basePrices_[instrument] = basePrice;
     119            1 :     if (lastPrices_.find(instrument) == lastPrices_.end()) {
     120            1 :         lastPrices_[instrument] = basePrice;
     121              :     }
     122            1 : }
     123              : 
     124           12 : void MockMdAdapter::run() {
     125           38 :     while (running_.load()) {
     126           26 :         std::vector<std::string> instruments;
     127              :         {
     128           26 :             std::lock_guard<std::mutex> lock(mutex_);
     129           26 :             instruments.assign(subscribedInstruments_.begin(), 
     130              :                              subscribedInstruments_.end());
     131           26 :         }
     132              : 
     133           59 :         for (const auto& inst : instruments) {
     134           33 :             if (!running_.load()) break;
     135              :             
     136           33 :             MarketData md = generateTick(inst);
     137           33 :             pushMarketData(std::move(md));
     138              :         }
     139              : 
     140           52 :         std::this_thread::sleep_for(std::chrono::milliseconds(tickIntervalMs_.load()));
     141           26 :     }
     142           12 : }
     143              : 
     144           33 : MarketData MockMdAdapter::generateTick(const std::string& instrument) {
     145           33 :     MarketData md;
     146              :     
     147              :     // 设置合约标识
     148           33 :     md.setInstrumentID(instrument.c_str());
     149           33 :     md.setExchangeID("MOCK");
     150           33 :     md.setTradingDay(tradingDay_.c_str());
     151           33 :     md.setUpdateTime(getCurrentTime().c_str());
     152           33 :     md.updateMillisec = std::chrono::duration_cast<std::chrono::milliseconds>(
     153           33 :         std::chrono::system_clock::now().time_since_epoch()
     154           33 :     ).count() % 1000;
     155              : 
     156              :     // 获取基准价和上次价格
     157           33 :     double basePrice = 5000.0;
     158           33 :     double lastPrice = 5000.0;
     159              :     {
     160           33 :         std::lock_guard<std::mutex> lock(mutex_);
     161           33 :         auto it = basePrices_.find(instrument);
     162           33 :         if (it != basePrices_.end()) {
     163           33 :             basePrice = it->second;
     164              :         }
     165           33 :         auto lit = lastPrices_.find(instrument);
     166           33 :         if (lit != lastPrices_.end()) {
     167           33 :             lastPrice = lit->second;
     168              :         }
     169           33 :     }
     170              : 
     171              :     // 生成随机价格变动
     172           33 :     const double vol = volatility_.load();
     173           33 :     std::uniform_real_distribution<double> dist(-vol, vol);
     174           33 :     double change = dist(rng_);
     175           33 :     double newPrice = lastPrice * (1.0 + change);
     176              :     
     177              :     // 限制价格在基准价的 ±10% 范围内
     178           33 :     double upperLimit = basePrice * 1.10;
     179           33 :     double lowerLimit = basePrice * 0.90;
     180           33 :     newPrice = std::max(lowerLimit, std::min(upperLimit, newPrice));
     181              : 
     182              :     // 更新最新价
     183              :     {
     184           33 :         std::lock_guard<std::mutex> lock(mutex_);
     185           33 :         lastPrices_[instrument] = newPrice;
     186           33 :     }
     187              : 
     188              :     // 设置价格信息
     189           33 :     md.lastPrice = newPrice;
     190           33 :     md.preSettlementPrice = basePrice;
     191           33 :     md.preClosePrice = basePrice * 0.998;
     192           33 :     md.openPrice = basePrice * 1.001;
     193           33 :     md.highestPrice = std::max(newPrice, basePrice * 1.02);
     194           33 :     md.lowestPrice = std::min(newPrice, basePrice * 0.98);
     195           33 :     md.closePrice = 0.0;  // 未收盘
     196           33 :     md.settlementPrice = 0.0;  // 未结算
     197           33 :     md.upperLimitPrice = upperLimit;
     198           33 :     md.lowerLimitPrice = lowerLimit;
     199           33 :     md.averagePrice = basePrice;
     200              : 
     201              :     // 生成成交信息
     202           33 :     std::uniform_int_distribution<int64_t> volDist(100, 10000);
     203           33 :     md.volume = volDist(rng_);
     204           33 :     md.turnover = md.volume * newPrice;
     205           33 :     md.openInterest = volDist(rng_) * 10;
     206           33 :     md.preOpenInterest = md.openInterest * 0.95;
     207              : 
     208              :     // 生成买卖盘
     209           33 :     double spread = basePrice * 0.0002;  // 0.02% 价差
     210           33 :     std::uniform_int_distribution<int32_t> qtyDist(10, 500);
     211              : 
     212           33 :     md.bidPrice1 = newPrice - spread;
     213           33 :     md.bidVolume1 = qtyDist(rng_);
     214           33 :     md.askPrice1 = newPrice + spread;
     215           33 :     md.askVolume1 = qtyDist(rng_);
     216              : 
     217           33 :     md.bidPrice2 = md.bidPrice1 - spread;
     218           33 :     md.bidVolume2 = qtyDist(rng_);
     219           33 :     md.askPrice2 = md.askPrice1 + spread;
     220           33 :     md.askVolume2 = qtyDist(rng_);
     221              : 
     222           33 :     md.bidPrice3 = md.bidPrice2 - spread;
     223           33 :     md.bidVolume3 = qtyDist(rng_);
     224           33 :     md.askPrice3 = md.askPrice2 + spread;
     225           33 :     md.askVolume3 = qtyDist(rng_);
     226              : 
     227           33 :     md.bidPrice4 = md.bidPrice3 - spread;
     228           33 :     md.bidVolume4 = qtyDist(rng_);
     229           33 :     md.askPrice4 = md.askPrice3 + spread;
     230           33 :     md.askVolume4 = qtyDist(rng_);
     231              : 
     232           33 :     md.bidPrice5 = md.bidPrice4 - spread;
     233           33 :     md.bidVolume5 = qtyDist(rng_);
     234           33 :     md.askPrice5 = md.askPrice4 + spread;
     235           33 :     md.askVolume5 = qtyDist(rng_);
     236              : 
     237           66 :     return md;
     238              : }
     239              : 
     240           36 : void MockMdAdapter::notifyState(MdAdapterState state, const std::string& message) {
     241           36 :     StateCallback callback;
     242              :     {
     243           36 :         std::lock_guard<std::mutex> lock(mutex_);
     244           36 :         callback = stateCallback_;
     245           36 :     }
     246              :     
     247           36 :     if (callback) {
     248            3 :         callback(state, message);
     249              :     }
     250           36 : }
     251              : 
     252           33 : std::string MockMdAdapter::getCurrentTime() const {
     253           33 :     auto now = std::chrono::system_clock::now();
     254           33 :     auto time = std::chrono::system_clock::to_time_t(now);
     255           33 :     std::tm tm = safe_localtime(time);
     256              :     
     257           33 :     std::ostringstream oss;
     258           33 :     oss << std::put_time(&tm, "%H:%M:%S");
     259           66 :     return oss.str();
     260           33 : }
     261              : 
     262              : } // namespace fix40
        

Generated by: LCOV version 2.0-1