Line data Source code
1 : /**
2 : * @file simulation_app.hpp
3 : * @brief 模拟交易网关 (Trade Gateway)
4 : *
5 : * 提供一个线程安全的 Application 实现,作为交易网关:
6 : * - 身份绑定:利用 FIX Logon 消息中的 SenderCompID 作为用户标识
7 : * - 安全路由:强制使用 Session 绑定的 UserID 进行验资和下单
8 : * - 协议扩展:支持 FIX User Defined Message (U系列) 实现自定义查询
9 : *
10 : * 集成以下模块:
11 : * - AccountManager: 账户管理
12 : * - PositionManager: 持仓管理
13 : * - InstrumentManager: 合约信息管理
14 : * - RiskManager: 风险控制
15 : *
16 : * @par 支持的消息类型
17 : * - D: NewOrderSingle (新订单)
18 : * - F: OrderCancelRequest (撤单请求)
19 : * - U1: BalanceQueryRequest (资金查询请求) - 自定义
20 : * - U3: PositionQueryRequest (持仓查询请求) - 自定义
21 : *
22 : * @par 发送的消息类型
23 : * - 8: ExecutionReport (执行报告)
24 : * - U2: BalanceQueryResponse (资金查询响应) - 自定义
25 : * - U4: PositionQueryResponse (持仓查询响应) - 自定义
26 : */
27 :
28 : #pragma once
29 :
30 : #include "fix/application.hpp"
31 : #include "fix/session_manager.hpp"
32 : #include "app/engine/matching_engine.hpp"
33 : #include "app/manager/account_manager.hpp"
34 : #include "app/manager/position_manager.hpp"
35 : #include "app/manager/instrument_manager.hpp"
36 : #include "app/manager/risk_manager.hpp"
37 : #include <memory>
38 : #include <unordered_map>
39 : #include <mutex>
40 :
41 : namespace fix40 {
42 :
43 : // 前向声明
44 : class IStore;
45 : struct SimulationAppTestAccess;
46 :
47 : /**
48 : * @class SimulationApp
49 : * @brief 模拟交易应用层
50 : *
51 : * 实现 Application 接口,采用生产者-消费者模式处理业务消息:
52 : * - fromApp() 将消息封装为事件,push 到无锁队列(生产者)
53 : * - MatchingEngine 在独立线程中消费并处理事件(消费者)
54 : *
55 : * 这种设计确保:
56 : * - Application 回调快速返回,不阻塞工作线程
57 : * - 所有订单处理在单线程中串行执行,无需加锁
58 : * - 异常隔离:撮合引擎的异常不会影响网络层
59 : *
60 : * @par 集成模块
61 : * - AccountManager: 账户管理(资金冻结/释放)
62 : * - PositionManager: 持仓管理(开仓/平仓)
63 : * - InstrumentManager: 合约信息管理
64 : * - RiskManager: 风险控制(资金/价格/持仓检查)
65 : *
66 : * @par 支持的消息类型
67 : * - D: NewOrderSingle (新订单)
68 : * - F: OrderCancelRequest (撤单请求)
69 : * - G: OrderCancelReplaceRequest (改单请求)
70 : *
71 : * @par 使用示例
72 : * @code
73 : * IStore* store = ...; // 存储接口
74 : * SimulationApp app(store);
75 : *
76 : * // 加载合约配置
77 : * app.getInstrumentManager().loadFromConfig("config/instruments.json");
78 : *
79 : * app.start(); // 启动撮合引擎
80 : *
81 : * session->set_application(&app);
82 : *
83 : * // 关闭时
84 : * app.stop();
85 : * @endcode
86 : */
87 : class SimulationApp : public Application {
88 : public:
89 : /**
90 : * @struct OrderMarginInfo
91 : * @brief 订单保证金信息
92 : *
93 : * 用于正确处理部分成交时的保证金计算。
94 : * 存储原始总冻结保证金和订单总数量,避免累计误差。
95 : */
96 : struct OrderMarginInfo {
97 : double originalFrozenMargin; ///< 原始总冻结保证金
98 : int64_t originalOrderQty; ///< 原始订单总数量
99 : double releasedMargin; ///< 已释放的保证金(累计)
100 :
101 3 : OrderMarginInfo()
102 3 : : originalFrozenMargin(0.0)
103 3 : , originalOrderQty(0)
104 3 : , releasedMargin(0.0) {}
105 :
106 8 : OrderMarginInfo(double frozen, int64_t qty)
107 8 : : originalFrozenMargin(frozen)
108 8 : , originalOrderQty(qty)
109 8 : , releasedMargin(0.0) {}
110 :
111 : /// 计算本次成交应释放的冻结保证金
112 12 : double calculateReleaseAmount(int64_t fillQty) {
113 12 : if (originalOrderQty <= 0) return 0.0;
114 11 : double amount = originalFrozenMargin * fillQty / originalOrderQty;
115 11 : releasedMargin += amount;
116 11 : return amount;
117 : }
118 :
119 : /// 获取剩余未释放的冻结保证金
120 6 : double getRemainingFrozen() const {
121 6 : return originalFrozenMargin - releasedMargin;
122 : }
123 : };
124 :
125 : /**
126 : * @brief 默认构造函数(不带持久化)
127 : */
128 : SimulationApp();
129 :
130 : /**
131 : * @brief 带存储接口的构造函数
132 : *
133 : * @param store 存储接口指针,用于账户和持仓的持久化
134 : */
135 : explicit SimulationApp(IStore* store);
136 :
137 : /**
138 : * @brief 析构函数
139 : *
140 : * 自动停止撮合引擎。
141 : */
142 : ~SimulationApp() override;
143 :
144 : /**
145 : * @brief 启动撮合引擎
146 : *
147 : * 必须在处理消息前调用。
148 : */
149 : void start();
150 :
151 : /**
152 : * @brief 停止撮合引擎
153 : *
154 : * 等待当前处理完成后退出。
155 : */
156 : void stop();
157 :
158 : // =========================================================================
159 : // Application 接口实现
160 : // =========================================================================
161 :
162 : /**
163 : * @brief 会话登录成功回调
164 : * @param sessionID 已建立的会话标识符
165 : *
166 : * 将登录事件提交到撮合引擎队列。
167 : */
168 : void onLogon(const SessionID& sessionID) override;
169 :
170 : /**
171 : * @brief 会话登出回调
172 : * @param sessionID 即将断开的会话标识符
173 : *
174 : * 将登出事件提交到撮合引擎队列。
175 : */
176 : void onLogout(const SessionID& sessionID) override;
177 :
178 : /**
179 : * @brief 收到业务消息回调
180 : * @param msg 收到的 FIX 业务消息
181 : * @param sessionID 消息来源的会话标识符
182 : *
183 : * 将消息封装为事件,提交到撮合引擎队列。
184 : * 此方法只做轻量的入队操作,快速返回。
185 : */
186 : void fromApp(const FixMessage& msg, const SessionID& sessionID) override;
187 :
188 : /**
189 : * @brief 发送业务消息前回调
190 : * @param msg 即将发送的 FIX 业务消息
191 : * @param sessionID 发送消息的会话标识符
192 : *
193 : * 用于记录审计日志。
194 : */
195 : void toApp(FixMessage& msg, const SessionID& sessionID) override;
196 :
197 : /**
198 : * @brief 获取会话管理器
199 : * @return SessionManager& 会话管理器引用
200 : *
201 : * 返回内部会话管理器的引用,调用者可通过该引用调用
202 : * registerSession()/unregisterSession() 等方法管理会话。
203 : */
204 4 : SessionManager& getSessionManager() { return sessionManager_; }
205 :
206 : /**
207 : * @brief 获取存储接口
208 : * @return IStore* 存储接口指针,可能为 nullptr
209 : *
210 : * 该指针用于账户/持仓等数据的持久化与重启恢复。
211 : */
212 0 : IStore* getStore() const override { return store_; }
213 :
214 : // =========================================================================
215 : // 管理器访问接口
216 : // =========================================================================
217 :
218 : /**
219 : * @brief 获取账户管理器
220 : * @return AccountManager& 账户管理器引用
221 : */
222 12 : AccountManager& getAccountManager() { return accountManager_; }
223 :
224 : /**
225 : * @brief 获取持仓管理器
226 : * @return PositionManager& 持仓管理器引用
227 : */
228 : PositionManager& getPositionManager() { return positionManager_; }
229 :
230 : /**
231 : * @brief 获取合约管理器
232 : * @return InstrumentManager& 合约管理器引用
233 : */
234 8 : InstrumentManager& getInstrumentManager() { return instrumentManager_; }
235 :
236 : /**
237 : * @brief 获取风控管理器
238 : * @return RiskManager& 风控管理器引用
239 : */
240 : RiskManager& getRiskManager() { return riskManager_; }
241 :
242 : /**
243 : * @brief 获取撮合引擎
244 : * @return MatchingEngine& 撮合引擎引用
245 : */
246 7 : MatchingEngine& getMatchingEngine() { return engine_; }
247 :
248 : // =========================================================================
249 : // 账户操作接口
250 : // =========================================================================
251 :
252 : /**
253 : * @brief 创建或获取账户
254 : *
255 : * 如果账户不存在,创建一个新账户;否则返回现有账户。
256 : *
257 : * @param accountId 账户ID
258 : * @param initialBalance 初始余额(仅在创建时使用)
259 : * @return Account 账户信息
260 : */
261 : Account getOrCreateAccount(const std::string& accountId, double initialBalance = 1000000.0);
262 :
263 : private:
264 : /**
265 : * @brief 单元测试访问器
266 : *
267 : * 仅用于单元测试访问内部实现细节(例如 pushAccountUpdate),避免在测试中使用
268 : * `#define private public` 破坏标准库头文件的可见性/封装并导致编译失败。
269 : */
270 : friend struct SimulationAppTestAccess;
271 :
272 : /**
273 : * @brief 初始化各管理器
274 : *
275 : * 设置撮合引擎与各管理器的关联。
276 : */
277 : void initializeManagers();
278 :
279 : /**
280 : * @brief ExecutionReport 回调处理
281 : * @param sessionID 目标会话
282 : * @param report 执行报告
283 : *
284 : * 将 ExecutionReport 转换为 FIX 消息并发送到客户端。
285 : * 同时更新账户和持仓状态。
286 : */
287 : void onExecutionReport(const SessionID& sessionID, const ExecutionReport& report);
288 :
289 : /**
290 : * @brief 处理订单成交
291 : *
292 : * 更新账户资金和持仓状态。
293 : *
294 : * @param accountId 账户ID
295 : * @param report 执行报告
296 : */
297 : void handleFill(const std::string& accountId, const ExecutionReport& report);
298 :
299 : /**
300 : * @brief 处理订单拒绝
301 : *
302 : * 释放冻结的保证金。
303 : *
304 : * @param accountId 账户ID
305 : * @param report 执行报告
306 : */
307 : void handleReject(const std::string& accountId, const ExecutionReport& report);
308 :
309 : /**
310 : * @brief 处理订单撤销
311 : *
312 : * 释放冻结的保证金。
313 : *
314 : * @param accountId 账户ID
315 : * @param report 执行报告
316 : */
317 : void handleCancel(const std::string& accountId, const ExecutionReport& report);
318 :
319 : /**
320 : * @brief 从SessionID提取账户ID
321 : *
322 : * 使用 SenderCompID 作为账户ID,实现身份绑定。
323 : *
324 : * @param sessionID 会话ID
325 : * @return 账户ID(使用SenderCompID)
326 : */
327 : std::string extractAccountId(const SessionID& sessionID) const;
328 :
329 : // =========================================================================
330 : // 消息处理函数 (Message Handlers)
331 : // =========================================================================
332 :
333 : /**
334 : * @brief 处理新订单 (MsgType = D)
335 : *
336 : * @param msg FIX 消息
337 : * @param sessionID 会话标识
338 : * @param userId 绑定的用户ID(从 Session 提取,非消息体)
339 : */
340 : void handleNewOrderSingle(const FixMessage& msg, const SessionID& sessionID, const std::string& userId);
341 :
342 : /**
343 : * @brief 处理撤单请求 (MsgType = F)
344 : *
345 : * @param msg FIX 消息
346 : * @param sessionID 会话标识
347 : * @param userId 绑定的用户ID
348 : */
349 : void handleOrderCancelRequest(const FixMessage& msg, const SessionID& sessionID, const std::string& userId);
350 :
351 : /**
352 : * @brief 处理资金查询请求 (MsgType = U1)
353 : *
354 : * 查询用户账户的资金信息,返回 U2 响应。
355 : *
356 : * @param msg FIX 消息
357 : * @param sessionID 会话标识
358 : * @param userId 绑定的用户ID
359 : */
360 : void handleBalanceQuery(const FixMessage& msg, const SessionID& sessionID, const std::string& userId);
361 :
362 : /**
363 : * @brief 处理持仓查询请求 (MsgType = U3)
364 : *
365 : * 查询用户的持仓信息,返回 U4 响应。
366 : *
367 : * @param msg FIX 消息
368 : * @param sessionID 会话标识
369 : * @param userId 绑定的用户ID
370 : */
371 : void handlePositionQuery(const FixMessage& msg, const SessionID& sessionID, const std::string& userId);
372 :
373 : /**
374 : * @brief 处理合约搜索请求 (MsgType = U7)
375 : *
376 : * 根据前缀搜索合约,返回 U8 响应。
377 : * 用于 Client 端的合约代码自动补全功能。
378 : *
379 : * @param msg FIX 消息
380 : * @param sessionID 会话标识
381 : */
382 : void handleInstrumentSearch(const FixMessage& msg, const SessionID& sessionID);
383 :
384 : /**
385 : * @brief 处理订单历史查询请求 (MsgType = U9)
386 : *
387 : * 从服务端持久化存储中加载该用户的历史订单,并返回 U10 响应。
388 : *
389 : * @param msg FIX 请求消息
390 : * @param sessionID 会话标识
391 : * @param userId 绑定的用户ID(从 Session 提取,非消息体)
392 : *
393 : * @note 由于 FIX RepeatingGroup 实现较复杂,本项目将订单列表序列化为文本放入 Text(58)。
394 : */
395 : void handleOrderHistoryQuery(const FixMessage& msg, const SessionID& sessionID, const std::string& userId);
396 :
397 : /**
398 : * @brief 发送拒绝消息
399 : *
400 : * 当收到无法处理的消息时,发送 BusinessMessageReject。
401 : *
402 : * @param sessionID 会话标识
403 : * @param refMsgType 被拒绝的消息类型
404 : * @param reason 拒绝原因
405 : */
406 : void sendBusinessReject(const SessionID& sessionID, const std::string& refMsgType, const std::string& reason);
407 :
408 : // =========================================================================
409 : // 行情驱动账户更新 (Market-Driven Account Update)
410 : // =========================================================================
411 :
412 : /**
413 : * @brief 处理行情更新,重算所有相关账户的价值
414 : *
415 : * 当行情变化时调用,更新持仓浮动盈亏和账户价值。
416 : * 可选择性地推送更新给相关 Client。
417 : *
418 : * @param instrumentId 合约代码
419 : * @param lastPrice 最新价
420 : */
421 : void onMarketDataUpdate(const std::string& instrumentId, double lastPrice);
422 :
423 : /**
424 : * @brief 向指定用户推送账户更新 (MsgType = U5)
425 : *
426 : * 当账户价值发生变化时,主动推送给 Client。
427 : *
428 : * @param userId 用户ID
429 : * @param reason 更新原因 (1=行情变化, 2=成交, 3=出入金)
430 : */
431 : void pushAccountUpdate(const std::string& userId, int reason = 1);
432 :
433 : /**
434 : * @brief 向指定用户推送持仓更新 (MsgType = U6)
435 : *
436 : * 当持仓发生变化时,主动推送给 Client。
437 : *
438 : * @param userId 用户ID
439 : * @param instrumentId 合约代码
440 : * @param reason 更新原因
441 : */
442 : void pushPositionUpdate(const std::string& userId, const std::string& instrumentId, int reason = 1);
443 :
444 : /**
445 : * @brief 根据用户ID查找对应的 SessionID
446 : *
447 : * @param userId 用户ID
448 : * @return std::optional<SessionID> 找到返回 SessionID,否则返回 nullopt
449 : */
450 : std::optional<SessionID> findSessionByUserId(const std::string& userId) const;
451 :
452 : // =========================================================================
453 : // 成员变量
454 : // =========================================================================
455 :
456 : MatchingEngine engine_; ///< 撮合引擎
457 : SessionManager sessionManager_; ///< 会话管理器
458 :
459 : AccountManager accountManager_; ///< 账户管理器
460 : PositionManager positionManager_; ///< 持仓管理器
461 : InstrumentManager instrumentManager_; ///< 合约管理器
462 : RiskManager riskManager_; ///< 风控管理器
463 :
464 : IStore* store_ = nullptr; ///< 存储接口(可为nullptr)
465 :
466 : /// 订单到账户的映射:clOrdID -> accountId
467 : std::unordered_map<std::string, std::string> orderAccountMap_;
468 :
469 : /// 订单保证金信息映射:clOrdID -> OrderMarginInfo
470 : std::unordered_map<std::string, OrderMarginInfo> orderMarginInfoMap_;
471 :
472 : /// 互斥锁,保护映射表
473 : mutable std::mutex mapMutex_;
474 : };
475 :
476 : } // namespace fix40
|