Line data Source code
1 : /**
2 : * @file matching_engine.hpp
3 : * @brief 撮合引擎接口
4 : *
5 : * 提供独立线程运行的撮合引擎,从无锁队列消费订单事件并处理。
6 : * 支持行情驱动撮合模式:用户订单与CTP行情盘口比对撮合。
7 : */
8 :
9 : #pragma once
10 :
11 : #include <thread>
12 : #include <atomic>
13 : #include <functional>
14 : #include <unordered_map>
15 : #include <list>
16 : #include <memory>
17 : #include "base/blockingconcurrentqueue.h"
18 : #include "app/engine/order_event.hpp"
19 : #include "app/engine/order_book.hpp"
20 : #include "app/model/market_data_snapshot.hpp"
21 : #include "market/market_data.hpp"
22 :
23 : namespace fix40 {
24 :
25 : // 前向声明
26 : class InstrumentManager;
27 :
28 : /**
29 : * @brief ExecutionReport 回调类型
30 : *
31 : * 当撮合引擎产生执行报告时调用此回调。
32 : * 参数:sessionID - 目标会话,report - 执行报告
33 : */
34 : using ExecutionReportCallback = std::function<void(const SessionID&, const ExecutionReport&)>;
35 :
36 : /**
37 : * @brief 行情更新回调类型
38 : *
39 : * 当行情数据更新时调用此回调,用于触发账户价值重算。
40 : * 参数:instrumentId - 合约代码,lastPrice - 最新价
41 : */
42 : using MarketDataUpdateCallback = std::function<void(const std::string&, double)>;
43 :
44 : /**
45 : * @class MatchingEngine
46 : * @brief 行情驱动撮合引擎
47 : *
48 : * 在独立线程中运行,从无锁队列消费订单事件并处理。
49 : *
50 : * @par 与原有撮合引擎的区别
51 : * - 原有:用户订单之间互相撮合
52 : * - 新版:用户订单与CTP行情盘口比对撮合
53 : *
54 : * @par 设计特点
55 : * - Application::fromApp() 只做轻量的入队操作,快速返回
56 : * - 所有订单处理在单线程中串行执行,无需加锁
57 : * - 工作线程不会因业务逻辑阻塞
58 : * - 支持行情驱动的被动撮合
59 : *
60 : * @par 撮合规则
61 : * - 买单成交条件:买价 >= CTP卖一价
62 : * - 卖单成交条件:卖价 <= CTP买一价
63 : * - 成交价格:取CTP对手盘价格
64 : *
65 : * @par 使用示例
66 : * @code
67 : * MatchingEngine engine;
68 : * engine.setExecutionReportCallback([](const SessionID& sid, const ExecutionReport& rpt) {
69 : * // 发送 ExecutionReport 到客户端
70 : * });
71 : * engine.start();
72 : *
73 : * // 从 Application::fromApp() 中提交事件
74 : * engine.submit(OrderEvent{OrderEventType::NEW_ORDER, sessionID, order});
75 : *
76 : * // 提交行情数据触发撮合
77 : * engine.submitMarketData(marketData);
78 : *
79 : * // 关闭时
80 : * engine.stop();
81 : * @endcode
82 : */
83 : class MatchingEngine {
84 : public:
85 : /**
86 : * @brief 构造撮合引擎
87 : */
88 : MatchingEngine();
89 :
90 : /**
91 : * @brief 析构函数
92 : *
93 : * 自动停止引擎线程。
94 : */
95 : ~MatchingEngine();
96 :
97 : // 禁止拷贝
98 : MatchingEngine(const MatchingEngine&) = delete;
99 : MatchingEngine& operator=(const MatchingEngine&) = delete;
100 :
101 : /**
102 : * @brief 启动撮合引擎线程
103 : */
104 : void start();
105 :
106 : /**
107 : * @brief 停止撮合引擎线程
108 : *
109 : * 等待当前处理完成后退出。
110 : */
111 : void stop();
112 :
113 : /**
114 : * @brief 提交订单事件
115 : * @param event 订单事件
116 : *
117 : * 线程安全,可从任意线程调用。
118 : * 事件会被放入无锁队列,由引擎线程异步处理。
119 : */
120 : void submit(const OrderEvent& event);
121 :
122 : /**
123 : * @brief 提交订单事件(移动语义)
124 : * @param event 订单事件
125 : */
126 : void submit(OrderEvent&& event);
127 :
128 : /**
129 : * @brief 检查引擎是否正在运行
130 : * @return true 正在运行
131 : * @return false 已停止
132 : */
133 3 : bool is_running() const { return running_.load(); }
134 :
135 : /**
136 : * @brief 设置 ExecutionReport 回调
137 : * @param callback 回调函数
138 : *
139 : * 必须在 start() 之前调用。
140 : */
141 23 : void setExecutionReportCallback(ExecutionReportCallback callback) {
142 23 : execReportCallback_ = std::move(callback);
143 23 : }
144 :
145 : /**
146 : * @brief 设置行情更新回调
147 : * @param callback 回调函数
148 : *
149 : * 当行情数据更新时调用,用于触发账户价值重算和推送。
150 : * 必须在 start() 之前调用。
151 : */
152 20 : void setMarketDataUpdateCallback(MarketDataUpdateCallback callback) {
153 20 : marketDataUpdateCallback_ = std::move(callback);
154 20 : }
155 :
156 : /**
157 : * @brief 获取订单簿(只读)
158 : * @param symbol 合约代码
159 : * @return const OrderBook* 订单簿指针,不存在返回 nullptr
160 : */
161 : const OrderBook* getOrderBook(const std::string& symbol) const;
162 :
163 : // =========================================================================
164 : // 行情驱动撮合接口
165 : // =========================================================================
166 :
167 : /**
168 : * @brief 提交行情数据
169 : *
170 : * 当新行情到达时,检查虚拟订单簿中是否有可成交的挂单。
171 : * 线程安全,可从任意线程调用。
172 : *
173 : * @param md 行情数据
174 : */
175 : void submitMarketData(const MarketData& md);
176 :
177 : /**
178 : * @brief 获取行情快照
179 : *
180 : * @param instrumentId 合约代码
181 : * @return const MarketDataSnapshot* 行情快照指针,不存在返回 nullptr
182 : */
183 : const MarketDataSnapshot* getMarketSnapshot(const std::string& instrumentId) const;
184 :
185 : /**
186 : * @brief 获取挂单列表(只读)
187 : *
188 : * @param instrumentId 合约代码
189 : * @return const std::list<Order>* 挂单列表指针,不存在返回 nullptr
190 : */
191 : const std::list<Order>* getPendingOrders(const std::string& instrumentId) const;
192 :
193 : /**
194 : * @brief 获取所有挂单数量
195 : *
196 : * @return 所有合约的挂单总数
197 : */
198 : size_t getTotalPendingOrderCount() const;
199 :
200 : // =========================================================================
201 : // 管理器设置(用于提供撮合所需的只读信息)
202 : // =========================================================================
203 :
204 : /**
205 : * @brief 设置合约管理器
206 : * @param instrumentMgr 合约管理器指针
207 : *
208 : * 撮合引擎会在行情更新时更新合约的涨跌停等信息。
209 : *
210 : * @note 账户/持仓/风控等业务权威逻辑由上层 Application(如 SimulationApp)负责,
211 : * 撮合引擎只负责撮合与订单状态推进。
212 : */
213 20 : void setInstrumentManager(InstrumentManager* instrumentMgr) { instrumentManager_ = instrumentMgr; }
214 :
215 : // =========================================================================
216 : // 行情驱动撮合核心方法(公开以便测试)
217 : // =========================================================================
218 :
219 : /**
220 : * @brief 检查买单是否可成交
221 : *
222 : * 买单成交条件:买价 >= CTP卖一价
223 : *
224 : * @param order 待检查的买单
225 : * @param snapshot 当前行情快照
226 : * @return 可成交返回 true
227 : */
228 : bool canMatchBuyOrder(const Order& order, const MarketDataSnapshot& snapshot) const;
229 :
230 : /**
231 : * @brief 检查卖单是否可成交
232 : *
233 : * 卖单成交条件:卖价 <= CTP买一价
234 : *
235 : * @param order 待检查的卖单
236 : * @param snapshot 当前行情快照
237 : * @return 可成交返回 true
238 : */
239 : bool canMatchSellOrder(const Order& order, const MarketDataSnapshot& snapshot) const;
240 :
241 : private:
242 : /**
243 : * @brief 引擎主循环
244 : */
245 : void run();
246 :
247 : /**
248 : * @brief 处理单个订单事件
249 : * @param event 订单事件
250 : */
251 : void process_event(const OrderEvent& event);
252 :
253 : /**
254 : * @brief 处理新订单(行情驱动模式)
255 : * @param event 订单事件
256 : *
257 : * 流程:
258 : * 1. 尝试立即撮合(与当前行情比对)
259 : * 2. 未成交部分挂入虚拟订单簿等待行情触发
260 : */
261 : void handle_new_order(const OrderEvent& event);
262 :
263 : /**
264 : * @brief 处理撤单请求
265 : * @param event 订单事件
266 : */
267 : void handle_cancel_request(const OrderEvent& event);
268 :
269 : /**
270 : * @brief 处理会话登录
271 : * @param event 事件
272 : */
273 : void handle_session_logon(const OrderEvent& event);
274 :
275 : /**
276 : * @brief 处理会话登出
277 : * @param event 事件
278 : */
279 : void handle_session_logout(const OrderEvent& event);
280 :
281 : /**
282 : * @brief 获取或创建订单簿
283 : * @param symbol 合约代码
284 : * @return OrderBook& 订单簿引用
285 : */
286 : OrderBook& getOrCreateOrderBook(const std::string& symbol);
287 :
288 : /**
289 : * @brief 发送 ExecutionReport
290 : * @param sessionID 目标会话
291 : * @param report 执行报告
292 : */
293 : void sendExecutionReport(const SessionID& sessionID, const ExecutionReport& report);
294 :
295 : /**
296 : * @brief 生成 ExecID
297 : */
298 : std::string generateExecID();
299 :
300 : /**
301 : * @brief 生成 OrderID
302 : */
303 : std::string generateOrderID();
304 :
305 : // =========================================================================
306 : // 行情驱动撮合内部方法
307 : // =========================================================================
308 :
309 : /**
310 : * @brief 处理行情更新
311 : *
312 : * 1. 更新行情快照
313 : * 2. 遍历该合约的挂单
314 : * 3. 检查是否满足成交条件
315 : * 4. 触发成交
316 : *
317 : * @param md 行情数据
318 : */
319 : void handleMarketData(const MarketData& md);
320 :
321 : /**
322 : * @brief 尝试撮合订单(行情驱动)
323 : *
324 : * @param order 待撮合订单
325 : * @param snapshot 当前行情快照
326 : * @return 是否成交
327 : */
328 : bool tryMatch(Order& order, const MarketDataSnapshot& snapshot);
329 :
330 : /**
331 : * @brief 执行成交
332 : *
333 : * @param order 成交的订单
334 : * @param fillPrice 成交价格
335 : * @param fillQty 成交数量
336 : */
337 : void executeFill(Order& order, double fillPrice, int64_t fillQty);
338 :
339 : /**
340 : * @brief 将订单添加到挂单列表
341 : *
342 : * @param order 待挂单的订单
343 : */
344 : void addToPendingOrders(const Order& order);
345 :
346 : /**
347 : * @brief 从挂单列表移除订单
348 : *
349 : * @param instrumentId 合约代码
350 : * @param clOrdID 客户订单ID
351 : * @return 被移除的订单,不存在返回 nullopt
352 : */
353 : std::optional<Order> removeFromPendingOrders(const std::string& instrumentId,
354 : const std::string& clOrdID);
355 :
356 : std::atomic<bool> running_{false}; ///< 运行状态
357 : std::thread worker_thread_; ///< 工作线程
358 :
359 : /// 订单事件队列(无锁阻塞队列)
360 : moodycamel::BlockingConcurrentQueue<OrderEvent> event_queue_;
361 :
362 : /// 订单簿映射:symbol -> OrderBook(保留用于兼容)
363 : std::unordered_map<std::string, std::unique_ptr<OrderBook>> orderBooks_;
364 :
365 : /// 订单到会话的映射:clOrdID -> SessionID(用于成交通知)
366 : std::unordered_map<std::string, SessionID> orderSessionMap_;
367 :
368 : /// 订单到用户ID的映射:clOrdID -> userId(用于日志/追踪,业务处理由上层负责)
369 : std::unordered_map<std::string, std::string> orderUserMap_;
370 :
371 : /// ExecutionReport 回调
372 : ExecutionReportCallback execReportCallback_;
373 :
374 : /// 行情更新回调
375 : MarketDataUpdateCallback marketDataUpdateCallback_;
376 :
377 : /// ExecID 计数器
378 : uint64_t nextExecID_ = 1;
379 :
380 : /// OrderID 计数器
381 : uint64_t nextOrderID_ = 1;
382 :
383 : // =========================================================================
384 : // 行情驱动撮合相关成员
385 : // =========================================================================
386 :
387 : /// 行情快照:instrumentId -> snapshot
388 : std::unordered_map<std::string, MarketDataSnapshot> marketSnapshots_;
389 :
390 : /// 虚拟订单簿(挂单列表):instrumentId -> 挂单列表
391 : std::unordered_map<std::string, std::list<Order>> pendingOrders_;
392 :
393 : /// 行情数据队列(无锁阻塞队列)
394 : moodycamel::BlockingConcurrentQueue<MarketData> marketDataQueue_;
395 :
396 : // =========================================================================
397 : // 管理器指针(用于提供撮合所需的只读信息)
398 : // =========================================================================
399 :
400 : InstrumentManager* instrumentManager_ = nullptr; ///< 合约管理器
401 : };
402 :
403 : } // namespace fix40
|