1use std::sync::atomic::{AtomicU32, Ordering};
2
3use futu_core::error::{FutuError, Result};
4use futu_core::proto_id;
5use futu_net::client::FutuClient;
6
7use crate::types::{ModifyOrderParams, PlaceOrderParams, PlaceOrderResult};
8
9static PACKET_SERIAL: AtomicU32 = AtomicU32::new(1);
11
12fn next_packet_id() -> futu_proto::common::PacketId {
13 let serial = PACKET_SERIAL.fetch_add(1, Ordering::Relaxed);
14 futu_proto::common::PacketId {
15 conn_id: 0, serial_no: serial,
17 }
18}
19
20pub async fn place_order(
25 client: &FutuClient,
26 params: &PlaceOrderParams,
27) -> Result<PlaceOrderResult> {
28 let req = futu_proto::trd_place_order::Request {
29 c2s: futu_proto::trd_place_order::C2s {
30 packet_id: next_packet_id(),
31 header: params.header.to_proto(),
32 trd_side: params.trd_side as i32,
33 order_type: params.order_type as i32,
34 code: params.code.clone(),
35 qty: params.qty,
36 price: params.price,
37 adjust_price: params.adjust_price,
38 adjust_side_and_limit: params.adjust_side_and_limit,
39 sec_market: None,
40 remark: None,
41 time_in_force: None,
42 fill_outside_rth: None,
43 aux_price: None,
44 trail_type: None,
45 trail_value: None,
46 trail_spread: None,
47 session: None,
48 position_id: None,
49 },
50 };
51
52 let body = prost::Message::encode_to_vec(&req);
53 let resp_frame = client.request(proto_id::TRD_PLACE_ORDER, body).await?;
54
55 let resp: futu_proto::trd_place_order::Response =
56 prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
57
58 if resp.ret_type != 0 {
59 return Err(FutuError::ServerError {
60 ret_type: resp.ret_type,
61 msg: resp.ret_msg.unwrap_or_default(),
62 });
63 }
64
65 let s2c = resp
66 .s2c
67 .ok_or(FutuError::Codec("missing s2c in PlaceOrder".into()))?;
68
69 Ok(PlaceOrderResult {
70 order_id: s2c.order_id.unwrap_or(0),
71 })
72}
73
74pub async fn modify_order(client: &FutuClient, params: &ModifyOrderParams) -> Result<u64> {
76 let req = futu_proto::trd_modify_order::Request {
77 c2s: futu_proto::trd_modify_order::C2s {
78 packet_id: next_packet_id(),
79 header: params.header.to_proto(),
80 order_id: params.order_id,
81 modify_order_op: params.modify_order_op as i32,
82 for_all: params.for_all,
83 trd_market: None,
84 qty: params.qty,
85 price: params.price,
86 adjust_price: None,
87 adjust_side_and_limit: None,
88 aux_price: None,
89 trail_type: None,
90 trail_value: None,
91 trail_spread: None,
92 order_id_ex: None,
93 },
94 };
95
96 let body = prost::Message::encode_to_vec(&req);
97 let resp_frame = client.request(proto_id::TRD_MODIFY_ORDER, body).await?;
98
99 let resp: futu_proto::trd_modify_order::Response =
100 prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
101
102 if resp.ret_type != 0 {
103 return Err(FutuError::ServerError {
104 ret_type: resp.ret_type,
105 msg: resp.ret_msg.unwrap_or_default(),
106 });
107 }
108
109 let s2c = resp
110 .s2c
111 .ok_or(FutuError::Codec("missing s2c in ModifyOrder".into()))?;
112
113 Ok(s2c.order_id)
114}
115
116pub async fn cancel_order(
118 client: &FutuClient,
119 header: &crate::types::TrdHeader,
120 order_id: u64,
121) -> Result<u64> {
122 modify_order(
123 client,
124 &ModifyOrderParams {
125 header: header.clone(),
126 order_id,
127 modify_order_op: crate::types::ModifyOrderOp::Cancel,
128 qty: None,
129 price: None,
130 for_all: None,
131 },
132 )
133 .await
134}