futu_trd/
account.rs

1use futu_core::error::{FutuError, Result};
2use futu_core::proto_id;
3use futu_net::client::FutuClient;
4
5use crate::types::{Funds, Position, TrdHeader};
6
7/// 查询账户资金
8pub 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
42/// 查询持仓列表
43pub 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
77/// 解锁交易
78///
79/// 下单前需要先解锁交易。
80/// `pwd_md5`: 交易密码的 MD5 值(32位小写十六进制字符串)
81pub 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
106/// 获取账户列表
107pub 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/// 交易账户信息
145#[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}