ctp_rust/api/
md_api.rs

1//! 行情API模块
2//!
3//! 提供期货行情数据订阅和接收功能
4
5use crate::api::{safe_cstr_to_string, to_cstring, CtpApi};
6use crate::encoding::GbkConverter;
7use crate::error::{CtpError, CtpResult};
8use crate::ffi::md_api::*;
9use crate::ffi::{CreateMdSpiBridge, MdSpiCallbacks};
10use crate::types::{ReqUserLoginField, RspInfoField, RspUserLoginField};
11use std::ffi::{c_void, CString};
12use std::os::raw::c_int;
13use std::ptr;
14use std::sync::{Arc, Mutex};
15
16// 行情API封装
17#[allow(dead_code)]
18pub struct MdApi {
19    // C++ API实例指针
20    api_ptr: *mut c_void,
21    // SPI实例指针
22    spi_ptr: *mut c_void,
23    // 是否已初始化
24    initialized: bool,
25    // 请求ID计数器
26    request_id: Arc<Mutex<i32>>,
27    // 回调处理器
28    handler: Option<Box<dyn MdSpiHandler + Send + Sync>>,
29}
30
31// 行情SPI回调处理器特质
32#[allow(unused_variables)]
33pub trait MdSpiHandler {
34    // 当客户端与交易后台建立起通信连接时(还未登录前),该方法被调用
35    fn on_front_connected(&mut self) {}
36
37    // 当客户端与交易后台通信连接断开时,该方法被调用
38    //
39    // # 参数
40    // * `reason` - 错误原因,0x2003 收到错误报文,0x2006 连接失败,0x2007 读失败,0x2008 写失败
41    fn on_front_disconnected(&mut self, reason: i32) {}
42
43    // 心跳超时警告,当长时间未收到报文时,该方法被调用
44    //
45    // # 参数
46    // * `time_lapse` - 距离上次接收报文的时间(秒)
47    fn on_heart_beat_warning(&mut self, time_lapse: i32) {}
48
49    // 登录请求响应
50    fn on_rsp_user_login(
51        &mut self,
52        user_login: Option<RspUserLoginField>,
53        rsp_info: Option<RspInfoField>,
54        request_id: i32,
55        is_last: bool,
56    ) {
57    }
58
59    // 登出请求响应
60    fn on_rsp_user_logout(
61        &mut self,
62        user_logout: Option<()>,
63        rsp_info: Option<RspInfoField>,
64        request_id: i32,
65        is_last: bool,
66    ) {
67    }
68
69    // 错误应答
70    fn on_rsp_error(&mut self, rsp_info: Option<RspInfoField>, request_id: i32, is_last: bool) {}
71
72    // 订阅行情应答
73    fn on_rsp_sub_market_data(
74        &mut self,
75        specific_instrument: Option<SpecificInstrumentField>,
76        rsp_info: Option<RspInfoField>,
77        request_id: i32,
78        is_last: bool,
79    ) {
80    }
81
82    // 取消订阅行情应答
83    fn on_rsp_unsub_market_data(
84        &mut self,
85        specific_instrument: Option<SpecificInstrumentField>,
86        rsp_info: Option<RspInfoField>,
87        request_id: i32,
88        is_last: bool,
89    ) {
90    }
91
92    // 深度行情通知
93    fn on_rtn_depth_market_data(&mut self, market_data: DepthMarketDataField) {}
94
95    // 询价通知
96    fn on_rtn_for_quote_rsp(&mut self, for_quote_rsp: ForQuoteRspField) {}
97}
98
99// 深度行情数据 - 必须与C++结构体CThostFtdcDepthMarketDataField完全匹配
100#[repr(C)]
101#[derive(Debug, Clone)]
102pub struct DepthMarketDataField {
103    /// 交易日
104    pub trading_day: [u8; 9],
105    /// 保留的无效字段
106    pub reserve1: [u8; 31],
107    /// 交易所代码
108    pub exchange_id: [u8; 9],
109    /// 保留的无效字段
110    pub reserve2: [u8; 31],
111    /// 最新价
112    pub last_price: f64,
113    /// 上次结算价
114    pub pre_settlement_price: f64,
115    /// 昨收盘
116    pub pre_close_price: f64,
117    /// 昨持仓量
118    pub pre_open_interest: f64,
119    /// 今开盘
120    pub open_price: f64,
121    /// 最高价
122    pub highest_price: f64,
123    /// 最低价
124    pub lowest_price: f64,
125    /// 数量
126    pub volume: i32,
127    /// 成交金额
128    pub turnover: f64,
129    /// 持仓量
130    pub open_interest: f64,
131    /// 今收盘
132    pub close_price: f64,
133    /// 本次结算价
134    pub settlement_price: f64,
135    /// 涨停板价
136    pub upper_limit_price: f64,
137    /// 跌停板价
138    pub lower_limit_price: f64,
139    /// 昨虚实度
140    pub pre_delta: f64,
141    /// 今虚实度
142    pub curr_delta: f64,
143    /// 最后修改时间
144    pub update_time: [u8; 9],
145    /// 最后修改毫秒
146    pub update_millisec: i32,
147    /// 申买价一
148    pub bid_price1: f64,
149    /// 申买量一
150    pub bid_volume1: i32,
151    /// 申卖价一
152    pub ask_price1: f64,
153    /// 申卖量一
154    pub ask_volume1: i32,
155    /// 申买价二
156    pub bid_price2: f64,
157    /// 申买量二
158    pub bid_volume2: i32,
159    /// 申卖价二
160    pub ask_price2: f64,
161    /// 申卖量二
162    pub ask_volume2: i32,
163    /// 申买价三
164    pub bid_price3: f64,
165    /// 申买量三
166    pub bid_volume3: i32,
167    /// 申卖价三
168    pub ask_price3: f64,
169    /// 申卖量三
170    pub ask_volume3: i32,
171    /// 申买价四
172    pub bid_price4: f64,
173    /// 申买量四
174    pub bid_volume4: i32,
175    /// 申卖价四
176    pub ask_price4: f64,
177    /// 申卖量四
178    pub ask_volume4: i32,
179    /// 申买价五
180    pub bid_price5: f64,
181    /// 申买量五
182    pub bid_volume5: i32,
183    /// 申卖价五
184    pub ask_price5: f64,
185    /// 申卖量五
186    pub ask_volume5: i32,
187    /// 当日均价
188    pub average_price: f64,
189    /// 业务日期
190    pub action_day: [u8; 9],
191    /// 合约代码
192    pub instrument_id: [u8; 81],
193    /// 合约在交易所的代码
194    pub exchange_inst_id: [u8; 81],
195    /// 上带价
196    pub banding_upper_price: f64,
197    /// 下带价
198    pub banding_lower_price: f64,
199}
200
201impl Default for DepthMarketDataField {
202    fn default() -> Self {
203        unsafe { std::mem::zeroed() }
204    }
205}
206
207impl DepthMarketDataField {
208    /// 获取合约代码的UTF-8字符串
209    pub fn get_instrument_id(&self) -> CtpResult<String> {
210        GbkConverter::fixed_bytes_to_utf8(&self.instrument_id)
211    }
212
213    /// 获取交易所代码的UTF-8字符串
214    pub fn get_exchange_id(&self) -> CtpResult<String> {
215        GbkConverter::fixed_bytes_to_utf8(&self.exchange_id)
216    }
217}
218
219// 指定的合约
220#[repr(C)]
221#[derive(Debug, Clone)]
222pub struct SpecificInstrumentField {
223    /// 保留的无效字段
224    pub reserve1: [u8; 31],
225    /// 合约代码
226    pub instrument_id: [u8; 81],
227}
228
229impl Default for SpecificInstrumentField {
230    fn default() -> Self {
231        unsafe { std::mem::zeroed() }
232    }
233}
234
235impl SpecificInstrumentField {
236    /// 获取合约代码的UTF-8字符串
237    pub fn get_instrument_id(&self) -> CtpResult<String> {
238        GbkConverter::fixed_bytes_to_utf8(&self.instrument_id)
239    }
240}
241
242// 询价响应
243#[repr(C)]
244#[derive(Debug, Clone)]
245pub struct ForQuoteRspField {
246    // 交易日
247    pub trading_day: [u8; 9],
248    // 合约代码
249    pub instrument_id: [u8; 31],
250    // 询价编号
251    pub for_quote_ref: [u8; 13],
252    // 用户代码
253    pub user_id: [u8; 16],
254    // 本地询价编号
255    pub for_quote_local_id: [u8; 13],
256    // 交易所代码
257    pub exchange_id: [u8; 9],
258    // 会员代码
259    pub participant_id: [u8; 11],
260    // 客户代码
261    pub client_id: [u8; 11],
262    // 合约在交易所的代码
263    pub exchange_inst_id: [u8; 31],
264    // 交易员代码
265    pub trader_id: [u8; 21],
266    // 安装编号
267    pub install_id: i32,
268    // 询价时间
269    pub insert_time: [u8; 9],
270    // 本地询价编号
271    pub for_quote_local_id2: [u8; 13],
272    // 业务日期
273    pub action_day: [u8; 9],
274}
275
276impl Default for ForQuoteRspField {
277    fn default() -> Self {
278        unsafe { std::mem::zeroed() }
279    }
280}
281
282unsafe impl Send for MdApi {}
283unsafe impl Sync for MdApi {}
284
285impl MdApi {
286    // 创建行情API实例
287    //
288    // # 参数
289    // * `flow_path` - 存储流文件的目录,默认为当前目录
290    // * `is_using_udp` - 是否使用UDP协议接收多播数据
291    // * `is_multicast` - 是否使用组播方式
292    // * `is_production_mode` - 是否使用生产版本API,true:生产版本 false:测评版本
293    pub fn new(
294        flow_path: Option<&str>,
295        is_using_udp: bool,
296        is_multicast: bool,
297        is_production_mode: bool,
298    ) -> CtpResult<Self> {
299        let flow_path_cstr = match flow_path {
300            Some(path) => Some(to_cstring(path)?),
301            None => None,
302        };
303
304        let flow_path_ptr = flow_path_cstr
305            .as_ref()
306            .map(|s| s.as_ptr())
307            .unwrap_or(ptr::null());
308
309        let api_ptr = unsafe {
310            CThostFtdcMdApi_CreateFtdcMdApi(
311                flow_path_ptr,
312                is_using_udp,
313                is_multicast,
314                is_production_mode,
315            )
316        };
317
318        if api_ptr.is_null() {
319            return Err(CtpError::InitializationError("创建行情API失败".to_string()));
320        }
321
322        Ok(MdApi {
323            api_ptr,
324            spi_ptr: ptr::null_mut(),
325            initialized: false,
326            request_id: Arc::new(Mutex::new(1)),
327            handler: None,
328        })
329    }
330
331    // 注册回调处理器
332    pub fn register_spi<T>(&mut self, handler: T) -> CtpResult<()>
333    where
334        T: MdSpiHandler + Send + Sync + 'static,
335    {
336        self.handler = Some(Box::new(handler));
337
338        // 创建回调结构体
339        let callbacks = MdSpiCallbacks {
340            user_data: self as *mut _ as *mut c_void,
341            on_front_connected: Some(on_front_connected_callback),
342            on_front_disconnected: Some(on_front_disconnected_callback),
343            on_heart_beat_warning: Some(on_heart_beat_warning_callback),
344            on_rsp_user_login: Some(on_rsp_user_login_callback),
345            on_rsp_user_logout: Some(on_rsp_user_logout_callback),
346            on_rsp_error: Some(on_rsp_error_callback),
347            on_rsp_sub_market_data: Some(on_rsp_sub_market_data_callback),
348            on_rsp_unsub_market_data: Some(on_rsp_unsub_market_data_callback),
349            on_rtn_depth_market_data: Some(on_rtn_depth_market_data_callback),
350            on_rtn_for_quote_rsp: Some(on_rtn_for_quote_rsp_callback),
351        };
352
353        // 创建SPI桥接器并注册到C++ API
354        self.spi_ptr = unsafe { CreateMdSpiBridge(&callbacks) };
355
356        if self.spi_ptr.is_null() {
357            return Err(CtpError::InitializationError(
358                "创建SPI桥接器失败".to_string(),
359            ));
360        }
361
362        // 注册SPI到CTP API
363        unsafe {
364            CThostFtdcMdApi_RegisterSpi(self.api_ptr, self.spi_ptr);
365        }
366
367        Ok(())
368    }
369
370    // 用户登录请求
371    pub fn req_user_login(&mut self, req: &ReqUserLoginField) -> CtpResult<i32> {
372        if self.api_ptr.is_null() {
373            return Err(CtpError::InitializationError("API未初始化".to_string()));
374        }
375        let request_id = self.next_request_id();
376
377        let result = unsafe {
378            CThostFtdcMdApi_ReqUserLogin(self.api_ptr, req as *const _ as *const c_void, request_id)
379        };
380        if result != 0 {
381            return Err(CtpError::FfiError(format!("登录请求失败: {}", result)));
382        }
383
384        Ok(request_id)
385    }
386
387    // 登出请求
388    pub fn req_user_logout(&mut self) -> CtpResult<i32> {
389        if self.api_ptr.is_null() {
390            return Err(CtpError::InitializationError("API未初始化".to_string()));
391        }
392
393        let request_id = self.next_request_id();
394
395        let result =
396            unsafe { CThostFtdcMdApi_ReqUserLogout(self.api_ptr, ptr::null(), request_id) };
397
398        if result != 0 {
399            return Err(CtpError::FfiError(format!("登出请求失败: {}", result)));
400        }
401
402        Ok(request_id)
403    }
404
405    // 订阅行情
406    //
407    // # 参数
408    // * `instrument_ids` - 合约代码数组
409    pub fn subscribe_market_data(&mut self, instrument_ids: &[&str]) -> CtpResult<()> {
410        if self.api_ptr.is_null() {
411            return Err(CtpError::InitializationError("API未初始化".to_string()));
412        }
413
414        // 转换为C字符串
415        let c_strings: Result<Vec<CString>, _> = instrument_ids
416            .iter()
417            .map(|&id| GbkConverter::utf8_to_gb18030_cstring(id))
418            .collect();
419
420        let c_strings = c_strings?;
421        let c_ptrs: Vec<*const i8> = c_strings.iter().map(|s| s.as_ptr()).collect();
422
423        let result = unsafe {
424            CThostFtdcMdApi_SubscribeMarketData(self.api_ptr, c_ptrs.as_ptr(), c_ptrs.len() as i32)
425        };
426
427        if result != 0 {
428            return Err(CtpError::FfiError(format!("订阅行情失败: {}", result)));
429        }
430
431        Ok(())
432    }
433
434    // 退订行情
435    //
436    // # 参数
437    // * `instrument_ids` - 合约代码数组
438    pub fn unsubscribe_market_data(&mut self, instrument_ids: &[&str]) -> CtpResult<()> {
439        if self.api_ptr.is_null() {
440            return Err(CtpError::InitializationError("API未初始化".to_string()));
441        }
442
443        // 转换为C字符串
444        let c_strings: Result<Vec<CString>, _> = instrument_ids
445            .iter()
446            .map(|&id| GbkConverter::utf8_to_gb18030_cstring(id))
447            .collect();
448
449        let c_strings = c_strings?;
450        let c_ptrs: Vec<*const i8> = c_strings.iter().map(|s| s.as_ptr()).collect();
451
452        let result = unsafe {
453            CThostFtdcMdApi_UnSubscribeMarketData(
454                self.api_ptr,
455                c_ptrs.as_ptr(),
456                c_ptrs.len() as i32,
457            )
458        };
459
460        if result != 0 {
461            return Err(CtpError::FfiError(format!("退订行情失败: {}", result)));
462        }
463
464        Ok(())
465    }
466
467    // 订阅询价
468    pub fn subscribe_for_quote_rsp(&mut self, instrument_ids: &[&str]) -> CtpResult<()> {
469        if self.api_ptr.is_null() {
470            return Err(CtpError::InitializationError("API未初始化".to_string()));
471        }
472
473        let c_strings: Result<Vec<CString>, _> = instrument_ids
474            .iter()
475            .map(|&id| GbkConverter::utf8_to_gb18030_cstring(id))
476            .collect();
477
478        let c_strings = c_strings?;
479        let c_ptrs: Vec<*const i8> = c_strings.iter().map(|s| s.as_ptr()).collect();
480
481        let result = unsafe {
482            CThostFtdcMdApi_SubscribeForQuoteRsp(self.api_ptr, c_ptrs.as_ptr(), c_ptrs.len() as i32)
483        };
484
485        if result != 0 {
486            return Err(CtpError::FfiError(format!("订阅询价失败: {}", result)));
487        }
488
489        Ok(())
490    }
491
492    // 退订询价
493    pub fn unsubscribe_for_quote_rsp(&mut self, instrument_ids: &[&str]) -> CtpResult<()> {
494        if self.api_ptr.is_null() {
495            return Err(CtpError::InitializationError("API未初始化".to_string()));
496        }
497
498        let c_strings: Result<Vec<CString>, _> = instrument_ids
499            .iter()
500            .map(|&id| GbkConverter::utf8_to_gb18030_cstring(id))
501            .collect();
502
503        let c_strings = c_strings?;
504        let c_ptrs: Vec<*const i8> = c_strings.iter().map(|s| s.as_ptr()).collect();
505
506        let result = unsafe {
507            CThostFtdcMdApi_UnSubscribeForQuoteRsp(
508                self.api_ptr,
509                c_ptrs.as_ptr(),
510                c_ptrs.len() as i32,
511            )
512        };
513
514        if result != 0 {
515            return Err(CtpError::FfiError(format!("退订询价失败: {}", result)));
516        }
517
518        Ok(())
519    }
520
521    // 获取下一个请求ID
522    fn next_request_id(&self) -> i32 {
523        let mut id = self.request_id.lock().unwrap();
524        let current = *id;
525        *id += 1;
526        current
527    }
528}
529
530impl CtpApi for MdApi {
531    fn get_version() -> CtpResult<String> {
532        let version_ptr = unsafe { CThostFtdcMdApi_GetApiVersion() };
533        safe_cstr_to_string(version_ptr)
534    }
535
536    fn init(&mut self) -> CtpResult<()> {
537        if self.api_ptr.is_null() {
538            return Err(CtpError::InitializationError("API指针为空".to_string()));
539        }
540
541        unsafe {
542            CThostFtdcMdApi_Init(self.api_ptr);
543        }
544
545        self.initialized = true;
546        Ok(())
547    }
548
549    fn release(&mut self) {
550        if !self.api_ptr.is_null() {
551            unsafe {
552                CThostFtdcMdApi_Release(self.api_ptr);
553            }
554            self.api_ptr = ptr::null_mut();
555        }
556        self.initialized = false;
557    }
558
559    fn get_trading_day(&self) -> CtpResult<String> {
560        if self.api_ptr.is_null() {
561            return Err(CtpError::InitializationError("API未初始化".to_string()));
562        }
563
564        let trading_day_ptr = unsafe { CThostFtdcMdApi_GetTradingDay(self.api_ptr) };
565
566        safe_cstr_to_string(trading_day_ptr)
567    }
568
569    fn register_front(&mut self, front_address: &str) -> CtpResult<()> {
570        if self.api_ptr.is_null() {
571            return Err(CtpError::InitializationError("API未初始化".to_string()));
572        }
573
574        let front_address_cstr = to_cstring(front_address)?;
575
576        unsafe {
577            CThostFtdcMdApi_RegisterFront(self.api_ptr, front_address_cstr.as_ptr());
578        }
579
580        Ok(())
581    }
582
583    fn join(&self) -> CtpResult<i32> {
584        if self.api_ptr.is_null() {
585            return Err(CtpError::InitializationError("API未初始化".to_string()));
586        }
587
588        let result = unsafe { CThostFtdcMdApi_Join(self.api_ptr) };
589
590        Ok(result)
591    }
592}
593
594impl Drop for MdApi {
595    fn drop(&mut self) {
596        self.release();
597    }
598}
599
600// 回调函数实现
601extern "C" fn on_front_connected_callback(user_data: *mut c_void) {
602    unsafe {
603        if let Some(api) = (user_data as *mut MdApi).as_mut() {
604            if let Some(ref mut handler) = api.handler {
605                handler.on_front_connected();
606            }
607        }
608    }
609}
610
611extern "C" fn on_front_disconnected_callback(user_data: *mut c_void, reason: c_int) {
612    unsafe {
613        if let Some(api) = (user_data as *mut MdApi).as_mut() {
614            if let Some(ref mut handler) = api.handler {
615                handler.on_front_disconnected(reason);
616            }
617        }
618    }
619}
620
621extern "C" fn on_heart_beat_warning_callback(user_data: *mut c_void, time_lapse: c_int) {
622    unsafe {
623        if let Some(api) = (user_data as *mut MdApi).as_mut() {
624            if let Some(ref mut handler) = api.handler {
625                handler.on_heart_beat_warning(time_lapse);
626            }
627        }
628    }
629}
630
631extern "C" fn on_rsp_user_login_callback(
632    user_data: *mut c_void,
633    user_login: *mut c_void,
634    rsp_info: *mut c_void,
635    request_id: c_int,
636    is_last: c_int,
637) {
638    unsafe {
639        if let Some(api) = (user_data as *mut MdApi).as_mut() {
640            if let Some(ref mut handler) = api.handler {
641                // 解析user_login指针
642                let parsed_user_login = if !user_login.is_null() {
643                    let login_ptr = user_login as *const RspUserLoginField;
644                    Some((*login_ptr).clone())
645                } else {
646                    None
647                };
648
649                // 解析rsp_info指针
650                let parsed_rsp_info = if !rsp_info.is_null() {
651                    let rsp_ptr = rsp_info as *const RspInfoField;
652                    Some((*rsp_ptr).clone())
653                } else {
654                    None
655                };
656
657                handler.on_rsp_user_login(
658                    parsed_user_login,
659                    parsed_rsp_info,
660                    request_id,
661                    is_last != 0,
662                );
663            }
664        }
665    }
666}
667
668extern "C" fn on_rsp_user_logout_callback(
669    user_data: *mut c_void,
670    _user_logout: *mut c_void,
671    _rsp_info: *mut c_void,
672    request_id: c_int,
673    is_last: c_int,
674) {
675    unsafe {
676        if let Some(api) = (user_data as *mut MdApi).as_mut() {
677            if let Some(ref mut handler) = api.handler {
678                // TODO: 解析user_logout和rsp_info结构体
679                handler.on_rsp_user_logout(None, None, request_id, is_last != 0);
680            }
681        }
682    }
683}
684
685extern "C" fn on_rsp_error_callback(
686    user_data: *mut c_void,
687    _rsp_info: *mut c_void,
688    request_id: c_int,
689    is_last: c_int,
690) {
691    unsafe {
692        if let Some(api) = (user_data as *mut MdApi).as_mut() {
693            if let Some(ref mut handler) = api.handler {
694                // TODO: 解析rsp_info结构体
695                handler.on_rsp_error(None, request_id, is_last != 0);
696            }
697        }
698    }
699}
700
701extern "C" fn on_rsp_sub_market_data_callback(
702    user_data: *mut c_void,
703    specific_instrument: *mut c_void,
704    rsp_info: *mut c_void,
705    request_id: c_int,
706    is_last: c_int,
707) {
708    unsafe {
709        if let Some(api) = (user_data as *mut MdApi).as_mut() {
710            if let Some(ref mut handler) = api.handler {
711                // 解析specific_instrument指针
712                let parsed_specific_instrument = if !specific_instrument.is_null() {
713                    let instrument_ptr = specific_instrument as *const SpecificInstrumentField;
714                    Some((*instrument_ptr).clone())
715                } else {
716                    None
717                };
718
719                // 解析rsp_info指针
720                let parsed_rsp_info = if !rsp_info.is_null() {
721                    let rsp_ptr = rsp_info as *const RspInfoField;
722                    Some((*rsp_ptr).clone())
723                } else {
724                    None
725                };
726
727                handler.on_rsp_sub_market_data(
728                    parsed_specific_instrument,
729                    parsed_rsp_info,
730                    request_id,
731                    is_last != 0,
732                );
733            }
734        }
735    }
736}
737
738extern "C" fn on_rsp_unsub_market_data_callback(
739    user_data: *mut c_void,
740    specific_instrument: *mut c_void,
741    rsp_info: *mut c_void,
742    request_id: c_int,
743    is_last: c_int,
744) {
745    unsafe {
746        if let Some(api) = (user_data as *mut MdApi).as_mut() {
747            if let Some(ref mut handler) = api.handler {
748                // 解析specific_instrument指针
749                let parsed_specific_instrument = if !specific_instrument.is_null() {
750                    let instrument_ptr = specific_instrument as *const SpecificInstrumentField;
751                    Some((*instrument_ptr).clone())
752                } else {
753                    None
754                };
755
756                // 解析rsp_info指针
757                let parsed_rsp_info = if !rsp_info.is_null() {
758                    let rsp_ptr = rsp_info as *const RspInfoField;
759                    Some((*rsp_ptr).clone())
760                } else {
761                    None
762                };
763
764                handler.on_rsp_unsub_market_data(
765                    parsed_specific_instrument,
766                    parsed_rsp_info,
767                    request_id,
768                    is_last != 0,
769                );
770            }
771        }
772    }
773}
774
775extern "C" fn on_rtn_depth_market_data_callback(user_data: *mut c_void, market_data: *mut c_void) {
776    unsafe {
777        if let Some(api) = (user_data as *mut MdApi).as_mut() {
778            if let Some(ref mut handler) = api.handler {
779                // 解析market_data指针
780                if !market_data.is_null() {
781                    let data_ptr = market_data as *const DepthMarketDataField;
782                    let parsed_data = (*data_ptr).clone();
783                    handler.on_rtn_depth_market_data(parsed_data);
784                }
785            }
786        }
787    }
788}
789
790extern "C" fn on_rtn_for_quote_rsp_callback(user_data: *mut c_void, _for_quote_rsp: *mut c_void) {
791    unsafe {
792        if let Some(api) = (user_data as *mut MdApi).as_mut() {
793            if let Some(ref mut handler) = api.handler {
794                // TODO: 解析for_quote_rsp结构体
795                // 临时创建一个空的for_quote_rsp
796                let temp_data = ForQuoteRspField::default();
797                handler.on_rtn_for_quote_rsp(temp_data);
798            }
799        }
800    }
801}
802
803#[cfg(test)]
804mod tests {
805    use super::*;
806
807    #[test]
808    fn test_version() {
809        // 测试版本获取函数的调用
810        match MdApi::get_version() {
811            Ok(version) => eprintln!("版本: {}", version),
812            Err(e) => eprintln!("获取版本失败: {}", e),
813        }
814    }
815}