futu_mcp/handlers/reference/
ownership.rs1use std::sync::Arc;
4
5use anyhow::{Result, anyhow, bail};
6use futu_net::client::FutuClient;
7use prost::Message;
8use serde::Serialize;
9
10use crate::state::parse_symbol;
11
12#[derive(Serialize)]
13struct ShareholdersOverviewOut {
14 symbol: String,
15 s2c: futu_proto::qot_get_shareholders_overview::S2c,
16}
17
18#[derive(Serialize)]
19struct ShareholdersHoldingChangesOut {
20 symbol: String,
21 s2c: futu_proto::qot_get_shareholders_holding_changes::S2c,
22}
23
24#[derive(Serialize)]
25struct ShareholdersHolderDetailOut {
26 symbol: String,
27 s2c: futu_proto::qot_get_shareholders_holder_detail::S2c,
28}
29
30#[derive(Serialize)]
31struct ShareholdersInstitutionalOut {
32 symbol: String,
33 s2c: futu_proto::qot_get_shareholders_institutional::S2c,
34}
35
36#[derive(Serialize)]
37struct InsiderHolderListOut {
38 symbol: String,
39 s2c: futu_proto::qot_get_insider_holder_list::S2c,
40}
41
42#[derive(Serialize)]
43struct InsiderTradeListOut {
44 symbol: String,
45 s2c: futu_proto::qot_get_insider_trade_list::S2c,
46}
47
48pub async fn get_shareholders_overview(
49 client: &Arc<FutuClient>,
50 symbol: &str,
51 period_id: Option<i32>,
52) -> Result<String> {
53 let sec = parse_symbol(symbol)?;
54 let req = futu_proto::qot_get_shareholders_overview::Request {
55 c2s: futu_proto::qot_get_shareholders_overview::C2s {
56 security: futu_proto::qot_common::Security {
57 market: sec.market as i32,
58 code: sec.code.clone(),
59 },
60 period_id,
61 },
62 };
63 let frame = client
64 .request(
65 futu_core::proto_id::QOT_GET_SHAREHOLDERS_OVERVIEW,
66 req.encode_to_vec(),
67 )
68 .await?;
69 let resp = futu_proto::qot_get_shareholders_overview::Response::decode(frame.body.as_ref())
70 .map_err(|e| anyhow!("decode shareholders_overview: {e}"))?;
71 if resp.ret_type != 0 {
72 bail!(
73 "shareholders_overview ret_type={} msg={:?}",
74 resp.ret_type,
75 resp.ret_msg
76 );
77 }
78 let s2c = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
79 Ok(serde_json::to_string_pretty(&ShareholdersOverviewOut {
80 symbol: symbol.to_string(),
81 s2c,
82 })?)
83}
84
85pub async fn get_shareholders_holding_changes(
86 client: &Arc<FutuClient>,
87 symbol: &str,
88 next_key: Option<String>,
89 num: Option<i32>,
90 sort_type: Option<i32>,
91 sort_column: Option<i32>,
92 filter_type: Option<i32>,
93) -> Result<String> {
94 let sec = parse_symbol(symbol)?;
95 let req = futu_proto::qot_get_shareholders_holding_changes::Request {
96 c2s: futu_proto::qot_get_shareholders_holding_changes::C2s {
97 security: futu_proto::qot_common::Security {
98 market: sec.market as i32,
99 code: sec.code.clone(),
100 },
101 next_key,
102 num,
103 sort_type,
104 sort_column,
105 filter_type,
106 },
107 };
108 let frame = client
109 .request(
110 futu_core::proto_id::QOT_GET_SHAREHOLDERS_HOLDING_CHANGES,
111 req.encode_to_vec(),
112 )
113 .await?;
114 let resp =
115 futu_proto::qot_get_shareholders_holding_changes::Response::decode(frame.body.as_ref())
116 .map_err(|e| anyhow!("decode shareholders_holding_changes: {e}"))?;
117 if resp.ret_type != 0 {
118 bail!(
119 "shareholders_holding_changes ret_type={} msg={:?}",
120 resp.ret_type,
121 resp.ret_msg
122 );
123 }
124 let s2c = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
125 Ok(serde_json::to_string_pretty(
126 &ShareholdersHoldingChangesOut {
127 symbol: symbol.to_string(),
128 s2c,
129 },
130 )?)
131}
132
133pub async fn get_shareholders_holder_detail(
134 client: &Arc<FutuClient>,
135 symbol: &str,
136 request_type: Option<i32>,
137 next_key: Option<String>,
138 num: Option<i32>,
139 sort_column: Option<i32>,
140 sort_type: Option<i32>,
141 period_id: Option<i32>,
142 holder_id: Option<i32>,
143) -> Result<String> {
144 let sec = parse_symbol(symbol)?;
145 let req = futu_proto::qot_get_shareholders_holder_detail::Request {
146 c2s: futu_proto::qot_get_shareholders_holder_detail::C2s {
147 security: futu_proto::qot_common::Security {
148 market: sec.market as i32,
149 code: sec.code.clone(),
150 },
151 request_type,
152 next_key,
153 num,
154 sort_column,
155 sort_type,
156 period_id,
157 holder_id,
158 },
159 };
160 let frame = client
161 .request(
162 futu_core::proto_id::QOT_GET_SHAREHOLDERS_HOLDER_DETAIL,
163 req.encode_to_vec(),
164 )
165 .await?;
166 let resp =
167 futu_proto::qot_get_shareholders_holder_detail::Response::decode(frame.body.as_ref())
168 .map_err(|e| anyhow!("decode shareholders_holder_detail: {e}"))?;
169 if resp.ret_type != 0 {
170 bail!(
171 "shareholders_holder_detail ret_type={} msg={:?}",
172 resp.ret_type,
173 resp.ret_msg
174 );
175 }
176 let s2c = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
177 Ok(serde_json::to_string_pretty(
178 &ShareholdersHolderDetailOut {
179 symbol: symbol.to_string(),
180 s2c,
181 },
182 )?)
183}
184
185pub async fn get_shareholders_institutional(
186 client: &Arc<FutuClient>,
187 symbol: &str,
188 next_key: Option<String>,
189 num: Option<i32>,
190) -> Result<String> {
191 let sec = parse_symbol(symbol)?;
192 let req = futu_proto::qot_get_shareholders_institutional::Request {
193 c2s: futu_proto::qot_get_shareholders_institutional::C2s {
194 security: futu_proto::qot_common::Security {
195 market: sec.market as i32,
196 code: sec.code.clone(),
197 },
198 next_key,
199 num,
200 },
201 };
202 let frame = client
203 .request(
204 futu_core::proto_id::QOT_GET_SHAREHOLDERS_INSTITUTIONAL,
205 req.encode_to_vec(),
206 )
207 .await?;
208 let resp =
209 futu_proto::qot_get_shareholders_institutional::Response::decode(frame.body.as_ref())
210 .map_err(|e| anyhow!("decode shareholders_institutional: {e}"))?;
211 if resp.ret_type != 0 {
212 bail!(
213 "shareholders_institutional ret_type={} msg={:?}",
214 resp.ret_type,
215 resp.ret_msg
216 );
217 }
218 let s2c = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
219 Ok(serde_json::to_string_pretty(
220 &ShareholdersInstitutionalOut {
221 symbol: symbol.to_string(),
222 s2c,
223 },
224 )?)
225}
226
227pub async fn get_insider_holder_list(
228 client: &Arc<FutuClient>,
229 symbol: &str,
230 next_key: Option<String>,
231 num: Option<i32>,
232) -> Result<String> {
233 let sec = parse_symbol(symbol)?;
234 let req = futu_proto::qot_get_insider_holder_list::Request {
235 c2s: futu_proto::qot_get_insider_holder_list::C2s {
236 security: futu_proto::qot_common::Security {
237 market: sec.market as i32,
238 code: sec.code.clone(),
239 },
240 next_key,
241 num,
242 },
243 };
244 let frame = client
245 .request(
246 futu_core::proto_id::QOT_GET_INSIDER_HOLDER_LIST,
247 req.encode_to_vec(),
248 )
249 .await?;
250 let resp = futu_proto::qot_get_insider_holder_list::Response::decode(frame.body.as_ref())
251 .map_err(|e| anyhow!("decode insider_holder_list: {e}"))?;
252 if resp.ret_type != 0 {
253 bail!(
254 "insider_holder_list ret_type={} msg={:?}",
255 resp.ret_type,
256 resp.ret_msg
257 );
258 }
259 let s2c = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
260 Ok(serde_json::to_string_pretty(&InsiderHolderListOut {
261 symbol: symbol.to_string(),
262 s2c,
263 })?)
264}
265
266pub async fn get_insider_trade_list(
267 client: &Arc<FutuClient>,
268 symbol: &str,
269 holder_id: Option<i64>,
270 next_key: Option<String>,
271 num: Option<i32>,
272) -> Result<String> {
273 let sec = parse_symbol(symbol)?;
274 let req = futu_proto::qot_get_insider_trade_list::Request {
275 c2s: futu_proto::qot_get_insider_trade_list::C2s {
276 security: futu_proto::qot_common::Security {
277 market: sec.market as i32,
278 code: sec.code.clone(),
279 },
280 holder_id,
281 next_key,
282 num,
283 },
284 };
285 let frame = client
286 .request(
287 futu_core::proto_id::QOT_GET_INSIDER_TRADE_LIST,
288 req.encode_to_vec(),
289 )
290 .await?;
291 let resp = futu_proto::qot_get_insider_trade_list::Response::decode(frame.body.as_ref())
292 .map_err(|e| anyhow!("decode insider_trade_list: {e}"))?;
293 if resp.ret_type != 0 {
294 bail!(
295 "insider_trade_list ret_type={} msg={:?}",
296 resp.ret_type,
297 resp.ret_msg
298 );
299 }
300 let s2c = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
301 Ok(serde_json::to_string_pretty(&InsiderTradeListOut {
302 symbol: symbol.to_string(),
303 s2c,
304 })?)
305}