Skip to main content

futu_mcp/handlers/reference/
short_info.rs

1//! MCP reference-data handlers for v10.6 short-info endpoints.
2
3use 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 CorporateActionsBuybacksOut {
14    symbol: String,
15    s2c: futu_proto::qot_get_corporate_actions_buybacks::S2c,
16}
17
18#[derive(Serialize)]
19struct CorporateActionsDividendsOut {
20    symbol: String,
21    s2c: futu_proto::qot_get_corporate_actions_dividends::S2c,
22}
23
24#[derive(Serialize)]
25struct CorporateActionsStockSplitsOut {
26    symbol: String,
27    s2c: futu_proto::qot_get_corporate_actions_stock_splits::S2c,
28}
29
30pub async fn get_corporate_actions_dividends(
31    client: &Arc<FutuClient>,
32    symbol: &str,
33) -> Result<String> {
34    let sec = parse_symbol(symbol)?;
35    let req = futu_proto::qot_get_corporate_actions_dividends::Request {
36        c2s: futu_proto::qot_get_corporate_actions_dividends::C2s {
37            security: futu_proto::qot_common::Security {
38                market: sec.market as i32,
39                code: sec.code.clone(),
40            },
41        },
42    };
43    let frame = client
44        .request(
45            futu_core::proto_id::QOT_GET_CORPORATE_ACTIONS_DIVIDENDS,
46            req.encode_to_vec(),
47        )
48        .await?;
49    let resp =
50        futu_proto::qot_get_corporate_actions_dividends::Response::decode(frame.body.as_ref())
51            .map_err(|e| anyhow!("decode corporate_actions_dividends: {e}"))?;
52    if resp.ret_type != 0 {
53        bail!(
54            "corporate_actions_dividends ret_type={} msg={:?}",
55            resp.ret_type,
56            resp.ret_msg
57        );
58    }
59    let s2c = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
60    Ok(serde_json::to_string_pretty(
61        &CorporateActionsDividendsOut {
62            symbol: symbol.to_string(),
63            s2c,
64        },
65    )?)
66}
67
68pub async fn get_corporate_actions_buybacks(
69    client: &Arc<FutuClient>,
70    symbol: &str,
71    next_key: Option<&str>,
72    num: Option<i32>,
73) -> Result<String> {
74    let sec = parse_symbol(symbol)?;
75    let req = futu_proto::qot_get_corporate_actions_buybacks::Request {
76        c2s: futu_proto::qot_get_corporate_actions_buybacks::C2s {
77            security: futu_proto::qot_common::Security {
78                market: sec.market as i32,
79                code: sec.code.clone(),
80            },
81            next_key: next_key.map(str::to_string),
82            num,
83        },
84    };
85    let frame = client
86        .request(
87            futu_core::proto_id::QOT_GET_CORPORATE_ACTIONS_BUYBACKS,
88            req.encode_to_vec(),
89        )
90        .await?;
91    let resp =
92        futu_proto::qot_get_corporate_actions_buybacks::Response::decode(frame.body.as_ref())
93            .map_err(|e| anyhow!("decode corporate_actions_buybacks: {e}"))?;
94    if resp.ret_type != 0 {
95        bail!(
96            "corporate_actions_buybacks ret_type={} msg={:?}",
97            resp.ret_type,
98            resp.ret_msg
99        );
100    }
101    let s2c = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
102    Ok(serde_json::to_string_pretty(
103        &CorporateActionsBuybacksOut {
104            symbol: symbol.to_string(),
105            s2c,
106        },
107    )?)
108}
109
110pub async fn get_corporate_actions_stock_splits(
111    client: &Arc<FutuClient>,
112    symbol: &str,
113    next_key: Option<&str>,
114    num: Option<i32>,
115) -> Result<String> {
116    let sec = parse_symbol(symbol)?;
117    let req = futu_proto::qot_get_corporate_actions_stock_splits::Request {
118        c2s: futu_proto::qot_get_corporate_actions_stock_splits::C2s {
119            security: futu_proto::qot_common::Security {
120                market: sec.market as i32,
121                code: sec.code.clone(),
122            },
123            next_key: next_key.map(str::to_string),
124            num,
125        },
126    };
127    let frame = client
128        .request(
129            futu_core::proto_id::QOT_GET_CORPORATE_ACTIONS_STOCK_SPLITS,
130            req.encode_to_vec(),
131        )
132        .await?;
133    let resp =
134        futu_proto::qot_get_corporate_actions_stock_splits::Response::decode(frame.body.as_ref())
135            .map_err(|e| anyhow!("decode corporate_actions_stock_splits: {e}"))?;
136    if resp.ret_type != 0 {
137        bail!(
138            "corporate_actions_stock_splits ret_type={} msg={:?}",
139            resp.ret_type,
140            resp.ret_msg
141        );
142    }
143    let s2c = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
144    Ok(serde_json::to_string_pretty(
145        &CorporateActionsStockSplitsOut {
146            symbol: symbol.to_string(),
147            s2c,
148        },
149    )?)
150}
151
152#[derive(Serialize)]
153struct DailyShortVolumeOut {
154    symbol: String,
155    s2c: futu_proto::qot_get_daily_short_volume::S2c,
156}
157
158pub async fn get_daily_short_volume(
159    client: &Arc<FutuClient>,
160    symbol: &str,
161    next_key: Option<&str>,
162    num: Option<i32>,
163) -> Result<String> {
164    let sec = parse_symbol(symbol)?;
165    let req = futu_proto::qot_get_daily_short_volume::Request {
166        c2s: futu_proto::qot_get_daily_short_volume::C2s {
167            security: futu_proto::qot_common::Security {
168                market: sec.market as i32,
169                code: sec.code.clone(),
170            },
171            next_key: next_key.map(str::to_string),
172            num,
173        },
174    };
175    let frame = client
176        .request(
177            futu_core::proto_id::QOT_GET_DAILY_SHORT_VOLUME,
178            req.encode_to_vec(),
179        )
180        .await?;
181    let resp = futu_proto::qot_get_daily_short_volume::Response::decode(frame.body.as_ref())
182        .map_err(|e| anyhow!("decode daily_short_volume: {e}"))?;
183    if resp.ret_type != 0 {
184        bail!(
185            "daily_short_volume ret_type={} msg={:?}",
186            resp.ret_type,
187            resp.ret_msg
188        );
189    }
190    let s2c = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
191    Ok(serde_json::to_string_pretty(&DailyShortVolumeOut {
192        symbol: symbol.to_string(),
193        s2c,
194    })?)
195}
196
197#[derive(Serialize)]
198struct ShortInterestOut {
199    symbol: String,
200    s2c: futu_proto::qot_get_short_interest::S2c,
201}
202
203pub async fn get_short_interest(
204    client: &Arc<FutuClient>,
205    symbol: &str,
206    next_key: Option<&str>,
207    num: Option<i32>,
208) -> Result<String> {
209    let sec = parse_symbol(symbol)?;
210    let req = futu_proto::qot_get_short_interest::Request {
211        c2s: futu_proto::qot_get_short_interest::C2s {
212            security: futu_proto::qot_common::Security {
213                market: sec.market as i32,
214                code: sec.code.clone(),
215            },
216            next_key: next_key.map(str::to_string),
217            num,
218        },
219    };
220    let frame = client
221        .request(
222            futu_core::proto_id::QOT_GET_SHORT_INTEREST,
223            req.encode_to_vec(),
224        )
225        .await?;
226    let resp = futu_proto::qot_get_short_interest::Response::decode(frame.body.as_ref())
227        .map_err(|e| anyhow!("decode short_interest: {e}"))?;
228    if resp.ret_type != 0 {
229        bail!(
230            "short_interest ret_type={} msg={:?}",
231            resp.ret_type,
232            resp.ret_msg
233        );
234    }
235    let s2c = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
236    Ok(serde_json::to_string_pretty(&ShortInterestOut {
237        symbol: symbol.to_string(),
238        s2c,
239    })?)
240}
241
242#[derive(Serialize)]
243struct TopTenBuySellBrokersOut {
244    symbol: String,
245    s2c: futu_proto::qot_get_top_ten_buy_sell_brokers::S2c,
246}
247
248pub async fn get_top_ten_buy_sell_brokers(
249    client: &Arc<FutuClient>,
250    symbol: &str,
251    days_before: Option<i32>,
252) -> Result<String> {
253    let sec = parse_symbol(symbol)?;
254    let req = futu_proto::qot_get_top_ten_buy_sell_brokers::Request {
255        c2s: futu_proto::qot_get_top_ten_buy_sell_brokers::C2s {
256            security: futu_proto::qot_common::Security {
257                market: sec.market as i32,
258                code: sec.code.clone(),
259            },
260            days_before,
261        },
262    };
263    let frame = client
264        .request(
265            futu_core::proto_id::QOT_GET_TOP_TEN_BUY_SELL_BROKERS,
266            req.encode_to_vec(),
267        )
268        .await?;
269    let resp = futu_proto::qot_get_top_ten_buy_sell_brokers::Response::decode(frame.body.as_ref())
270        .map_err(|e| anyhow!("decode top_ten_buy_sell_brokers: {e}"))?;
271    if resp.ret_type != 0 {
272        bail!(
273            "top_ten_buy_sell_brokers ret_type={} msg={:?}",
274            resp.ret_type,
275            resp.ret_msg
276        );
277    }
278    let s2c = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
279    Ok(serde_json::to_string_pretty(&TopTenBuySellBrokersOut {
280        symbol: symbol.to_string(),
281        s2c,
282    })?)
283}