Skip to main content

futu_trd/
push.rs

1use async_trait::async_trait;
2use futu_core::error::FutuError;
3use futu_core::proto_id;
4
5/// 交易推送回调 trait
6#[async_trait]
7pub trait TradeHandler: Send + Sync + 'static {
8    /// 订单状态变更推送
9    async fn on_order_update(&self, _header: OrderUpdateInfo) {}
10
11    /// 成交推送
12    async fn on_order_fill_update(&self, _header: OrderFillUpdateInfo) {}
13
14    /// 交易通知推送
15    async fn on_trd_notify(&self, _info: TrdNotifyInfo) {}
16}
17
18/// 订单更新信息
19#[derive(Debug, Clone)]
20pub struct OrderUpdateInfo {
21    /// 交易环境(对齐 proto `TrdCommon.TrdEnv`)
22    pub trd_env: i32,
23    /// 订单所属账户 ID
24    pub acc_id: u64,
25    /// 交易市场(对齐 proto `TrdCommon.TrdMarket`)
26    pub trd_market: i32,
27    // 订单详情由调用方根据需要从 proto 解析
28    /// 原始 proto body,调用方自行 `prost::Message::decode`
29    pub raw_body: bytes::Bytes,
30}
31
32/// 成交更新信息
33#[derive(Debug, Clone)]
34pub struct OrderFillUpdateInfo {
35    /// 交易环境
36    pub trd_env: i32,
37    /// 成交所属账户 ID
38    pub acc_id: u64,
39    /// 交易市场
40    pub trd_market: i32,
41    /// 原始 proto body,调用方自行 `prost::Message::decode`
42    pub raw_body: bytes::Bytes,
43}
44
45/// 交易推送分发器
46pub struct TradePushDispatcher;
47
48impl TradePushDispatcher {
49    /// 分发交易推送消息
50    pub async fn dispatch(
51        handler: &dyn TradeHandler,
52        proto_id_val: u32,
53        body: &[u8],
54    ) -> Result<(), FutuError> {
55        match proto_id_val {
56            proto_id::TRD_UPDATE_ORDER => {
57                let resp: futu_proto::trd_update_order::Response =
58                    prost::Message::decode(body).map_err(FutuError::Proto)?;
59                if let Some(s2c) = &resp.s2c {
60                    let info = OrderUpdateInfo {
61                        trd_env: s2c.header.trd_env,
62                        acc_id: s2c.header.acc_id,
63                        trd_market: s2c.header.trd_market,
64                        raw_body: bytes::Bytes::copy_from_slice(body),
65                    };
66                    handler.on_order_update(info).await;
67                }
68            }
69            proto_id::TRD_UPDATE_ORDER_FILL => {
70                let resp: futu_proto::trd_update_order_fill::Response =
71                    prost::Message::decode(body).map_err(FutuError::Proto)?;
72                if let Some(s2c) = &resp.s2c {
73                    let info = OrderFillUpdateInfo {
74                        trd_env: s2c.header.trd_env,
75                        acc_id: s2c.header.acc_id,
76                        trd_market: s2c.header.trd_market,
77                        raw_body: bytes::Bytes::copy_from_slice(body),
78                    };
79                    handler.on_order_fill_update(info).await;
80                }
81            }
82            proto_id::TRD_NOTIFY => {
83                let resp: futu_proto::trd_notify::Response =
84                    prost::Message::decode(body).map_err(FutuError::Proto)?;
85                if let Some(s2c) = &resp.s2c {
86                    let info = TrdNotifyInfo {
87                        trd_env: s2c.header.trd_env,
88                        acc_id: s2c.header.acc_id,
89                        trd_market: s2c.header.trd_market,
90                        notify_type: s2c.r#type,
91                    };
92                    handler.on_trd_notify(info).await;
93                }
94            }
95            _ => {
96                tracing::debug!(proto_id = proto_id_val, "unhandled trade push");
97            }
98        }
99        Ok(())
100    }
101}
102
103/// 交易通知信息
104#[derive(Debug, Clone)]
105pub struct TrdNotifyInfo {
106    /// 交易环境
107    pub trd_env: i32,
108    /// 通知所属账户 ID
109    pub acc_id: u64,
110    /// 交易市场
111    pub trd_market: i32,
112    /// 通知类型(对齐 proto `TrdNotify.NotifyType`:订单状态 / 成交 / 资金变化等)
113    pub notify_type: i32,
114}