1use futu_core::error::{FutuError, Result};
2use futu_core::proto_id;
3use futu_net::client::FutuClient;
4
5use crate::types::{Funds, Position, TrdHeader};
6
7pub async fn get_funds(client: &FutuClient, header: &TrdHeader) -> Result<Funds> {
9 let req = futu_proto::trd_get_funds::Request {
10 c2s: futu_proto::trd_get_funds::C2s {
11 header: header.to_proto(),
12 refresh_cache: None,
13 currency: None,
14 asset_category: None,
15 },
16 };
17
18 let body = prost::Message::encode_to_vec(&req);
19 let resp_frame = client.request(proto_id::TRD_GET_FUNDS, body).await?;
20
21 let resp: futu_proto::trd_get_funds::Response =
22 prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
23
24 if resp.ret_type != 0 {
25 return Err(FutuError::ServerError {
26 ret_type: resp.ret_type,
27 msg: resp.ret_msg.unwrap_or_default(),
28 });
29 }
30
31 let s2c = resp
32 .s2c
33 .ok_or(FutuError::Codec("missing s2c in GetFunds".into()))?;
34
35 let funds = s2c
36 .funds
37 .ok_or(FutuError::Codec("missing funds in GetFunds".into()))?;
38
39 Ok(Funds::from_proto(&funds))
40}
41
42pub async fn get_position_list(client: &FutuClient, header: &TrdHeader) -> Result<Vec<Position>> {
44 let req = futu_proto::trd_get_position_list::Request {
45 c2s: futu_proto::trd_get_position_list::C2s {
46 header: header.to_proto(),
47 filter_conditions: None,
48 filter_pl_ratio_min: None,
49 filter_pl_ratio_max: None,
50 refresh_cache: None,
51 asset_category: None,
52 },
53 };
54
55 let body = prost::Message::encode_to_vec(&req);
56 let resp_frame = client
57 .request(proto_id::TRD_GET_POSITION_LIST, body)
58 .await?;
59
60 let resp: futu_proto::trd_get_position_list::Response =
61 prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
62
63 if resp.ret_type != 0 {
64 return Err(FutuError::ServerError {
65 ret_type: resp.ret_type,
66 msg: resp.ret_msg.unwrap_or_default(),
67 });
68 }
69
70 let s2c = resp
71 .s2c
72 .ok_or(FutuError::Codec("missing s2c in GetPositionList".into()))?;
73
74 Ok(s2c.position_list.iter().map(Position::from_proto).collect())
75}
76
77pub async fn unlock_trade(client: &FutuClient, pwd_md5: &str, is_unlock: bool) -> Result<()> {
82 let req = futu_proto::trd_unlock_trade::Request {
83 c2s: futu_proto::trd_unlock_trade::C2s {
84 unlock: is_unlock,
85 pwd_md5: Some(pwd_md5.to_string()),
86 security_firm: None,
87 },
88 };
89
90 let body = prost::Message::encode_to_vec(&req);
91 let resp_frame = client.request(proto_id::TRD_UNLOCK_TRADE, body).await?;
92
93 let resp: futu_proto::trd_unlock_trade::Response =
94 prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
95
96 if resp.ret_type != 0 {
97 return Err(FutuError::ServerError {
98 ret_type: resp.ret_type,
99 msg: resp.ret_msg.unwrap_or_default(),
100 });
101 }
102
103 Ok(())
104}
105
106pub async fn get_acc_list(client: &FutuClient) -> Result<Vec<TrdAcc>> {
108 let req = futu_proto::trd_get_acc_list::Request {
109 c2s: futu_proto::trd_get_acc_list::C2s {
110 user_id: 0,
111 trd_category: None,
112 need_general_sec_account: None,
113 },
114 };
115
116 let body = prost::Message::encode_to_vec(&req);
117 let resp_frame = client.request(proto_id::TRD_GET_ACC_LIST, body).await?;
118
119 let resp: futu_proto::trd_get_acc_list::Response =
120 prost::Message::decode(resp_frame.body.as_ref()).map_err(FutuError::Proto)?;
121
122 if resp.ret_type != 0 {
123 return Err(FutuError::ServerError {
124 ret_type: resp.ret_type,
125 msg: resp.ret_msg.unwrap_or_default(),
126 });
127 }
128
129 let s2c = resp
130 .s2c
131 .ok_or(FutuError::Codec("missing s2c in GetAccList".into()))?;
132
133 Ok(s2c
134 .acc_list
135 .iter()
136 .map(|a| TrdAcc {
137 trd_env: a.trd_env,
138 acc_id: a.acc_id,
139 trd_market_auth_list: a.trd_market_auth_list.clone(),
140 })
141 .collect())
142}
143
144#[derive(Debug, Clone)]
146pub struct TrdAcc {
147 pub trd_env: i32,
148 pub acc_id: u64,
149 pub trd_market_auth_list: Vec<i32>,
150}