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
|