Skip to main content

futu_mcp/tools/
reference_f10.rs

1//! MCP F10, financials, research, and valuation reference tools.
2
3use crate::handlers;
4use crate::tool_args::*;
5use rmcp::{
6    RoleServer, handler::server::wrapper::Parameters, service::RequestContext, tool, tool_router,
7};
8
9use super::FutuServer;
10
11#[tool_router(router = reference_f10_tool_router, vis = "pub(crate)")]
12impl FutuServer {
13    #[tool(
14        description = "Company operational efficiency metrics such as employees and per-capita income/profit. Futu API v10.6: OpenQuoteContext.get_company_operational_efficiency."
15    )]
16    async fn futu_get_company_operational_efficiency(
17        &self,
18        Parameters(req): Parameters<CompanyOperationalEfficiencyReq>,
19        req_ctx: RequestContext<RoleServer>,
20    ) -> std::result::Result<String, String> {
21        req.validate()?;
22        tracing::info!(
23            tool = "futu_get_company_operational_efficiency",
24            symbol = %req.symbol,
25            next_key = %crate::state::audit_fmt::opt_str(req.next_key.as_deref()),
26            num = ?req.num,
27            currency_code = %crate::state::audit_fmt::opt_str(req.currency_code.as_deref()),
28            financial_type = ?req.financial_type
29        );
30        let client = self
31            .read_client_or_err(
32                "futu_get_company_operational_efficiency",
33                &req_ctx,
34                None,
35                None,
36            )
37            .await?;
38        Self::wrap_result(
39            handlers::reference::get_company_operational_efficiency(
40                &client,
41                &req.symbol,
42                req.next_key.as_deref(),
43                req.num,
44                req.currency_code.as_deref(),
45                req.financial_type,
46            )
47            .await,
48        )
49    }
50
51    #[tool(
52        description = "Price move around financial earnings announcement dates. Futu API v10.6: OpenQuoteContext.get_financials_earnings_price_move."
53    )]
54    async fn futu_get_financials_earnings_price_move(
55        &self,
56        Parameters(req): Parameters<FinancialsEarningsPriceMoveReq>,
57        req_ctx: RequestContext<RoleServer>,
58    ) -> std::result::Result<String, String> {
59        req.validate()?;
60        tracing::info!(
61            tool = "futu_get_financials_earnings_price_move",
62            symbol = %req.symbol,
63            period_count = ?req.period_count
64        );
65        let client = self
66            .read_client_or_err(
67                "futu_get_financials_earnings_price_move",
68                &req_ctx,
69                None,
70                None,
71            )
72            .await?;
73        Self::wrap_result(
74            handlers::reference::get_financials_earnings_price_move(
75                &client,
76                &req.symbol,
77                req.period_count,
78            )
79            .await,
80        )
81    }
82
83    #[tool(
84        description = "Price history around financial earnings announcement dates. Futu API v10.6: OpenQuoteContext.get_financials_earnings_price_history."
85    )]
86    async fn futu_get_financials_earnings_price_history(
87        &self,
88        Parameters(req): Parameters<FinancialsEarningsPriceHistoryReq>,
89        req_ctx: RequestContext<RoleServer>,
90    ) -> std::result::Result<String, String> {
91        tracing::info!(
92            tool = "futu_get_financials_earnings_price_history",
93            symbol = %req.symbol
94        );
95        let client = self
96            .read_client_or_err(
97                "futu_get_financials_earnings_price_history",
98                &req_ctx,
99                None,
100                None,
101            )
102            .await?;
103        Self::wrap_result(
104            handlers::reference::get_financials_earnings_price_history(&client, &req.symbol).await,
105        )
106    }
107
108    #[tool(
109        description = "Read-only financial calendar view. Rust enhancement backed by the mobile calendar service; not a C++ FTAPI parity endpoint."
110    )]
111    async fn futu_get_financial_calendar(
112        &self,
113        Parameters(req): Parameters<FinancialCalendarReq>,
114        req_ctx: RequestContext<RoleServer>,
115    ) -> std::result::Result<String, String> {
116        req.validate()?;
117        tracing::info!(
118            tool = "futu_get_financial_calendar",
119            begin_date = %req.begin_date,
120            end_date = %req.end_date,
121            markets = ?req.market_list,
122            count = ?req.count,
123            watchlist_stock_ids = req.watchlist_stock_ids.len(),
124            holding_stock_ids = req.holding_stock_ids.len()
125        );
126        let client = self
127            .read_client_or_err("futu_get_financial_calendar", &req_ctx, None, None)
128            .await?;
129        Self::wrap_result(
130            handlers::reference::get_financial_calendar(&client, req.to_proto()).await,
131        )
132    }
133
134    #[tool(
135        description = "Target-stock financial calendar search. Rust enhancement backed by the mobile calendar service; not a C++ FTAPI parity endpoint."
136    )]
137    async fn futu_search_target_financial_calendar(
138        &self,
139        Parameters(req): Parameters<TargetFinancialCalendarReq>,
140        req_ctx: RequestContext<RoleServer>,
141    ) -> std::result::Result<String, String> {
142        req.validate()?;
143        tracing::info!(
144            tool = "futu_search_target_financial_calendar",
145            stock_ids = req.stock_id.len(),
146            markets = ?req.market_list,
147            size = req.size,
148            start = ?req.start
149        );
150        let client = self
151            .read_client_or_err(
152                "futu_search_target_financial_calendar",
153                &req_ctx,
154                None,
155                None,
156            )
157            .await?;
158        Self::wrap_result(
159            handlers::reference::search_target_financial_calendar(&client, req.to_proto()).await,
160        )
161    }
162
163    #[tool(
164        description = "Financial statements. Futu API v10.6: OpenQuoteContext.get_financials_statements."
165    )]
166    async fn futu_get_financials_statements(
167        &self,
168        Parameters(req): Parameters<FinancialsStatementsReq>,
169        req_ctx: RequestContext<RoleServer>,
170    ) -> std::result::Result<String, String> {
171        req.validate()?;
172        tracing::info!(
173            tool = "futu_get_financials_statements",
174            symbol = %req.symbol,
175            statement_type = ?req.statement_type,
176            financial_type = ?req.financial_type,
177            currency_code = ?req.currency_code,
178            next_key = ?req.next_key,
179            num = ?req.num
180        );
181        let client = self
182            .read_client_or_err("futu_get_financials_statements", &req_ctx, None, None)
183            .await?;
184        Self::wrap_result(
185            handlers::reference::get_financials_statements(
186                &client,
187                &req.symbol,
188                req.statement_type,
189                req.financial_type,
190                req.currency_code.as_deref(),
191                req.next_key.as_deref(),
192                req.num,
193            )
194            .await,
195        )
196    }
197
198    #[tool(
199        description = "Financials revenue breakdown. Futu API v10.6: OpenQuoteContext.get_financials_revenue_breakdown."
200    )]
201    async fn futu_get_financials_revenue_breakdown(
202        &self,
203        Parameters(req): Parameters<FinancialsRevenueBreakdownReq>,
204        req_ctx: RequestContext<RoleServer>,
205    ) -> std::result::Result<String, String> {
206        req.validate()?;
207        tracing::info!(
208            tool = "futu_get_financials_revenue_breakdown",
209            symbol = %req.symbol,
210            date = ?req.date,
211            financial_type = ?req.financial_type,
212            currency_code = ?req.currency_code
213        );
214        let client = self
215            .read_client_or_err(
216                "futu_get_financials_revenue_breakdown",
217                &req_ctx,
218                None,
219                None,
220            )
221            .await?;
222        Self::wrap_result(
223            handlers::reference::get_financials_revenue_breakdown(
224                &client,
225                &req.symbol,
226                req.date,
227                req.financial_type,
228                req.currency_code.as_deref(),
229            )
230            .await,
231        )
232    }
233
234    #[tool(
235        description = "Research analyst consensus. Futu API v10.6: OpenQuoteContext.get_research_analyst_consensus."
236    )]
237    async fn futu_get_research_analyst_consensus(
238        &self,
239        Parameters(req): Parameters<ResearchAnalystConsensusReq>,
240        req_ctx: RequestContext<RoleServer>,
241    ) -> std::result::Result<String, String> {
242        tracing::info!(
243            tool = "futu_get_research_analyst_consensus",
244            symbol = %req.symbol
245        );
246        let client = self
247            .read_client_or_err("futu_get_research_analyst_consensus", &req_ctx, None, None)
248            .await?;
249        Self::wrap_result(
250            handlers::reference::get_research_analyst_consensus(&client, &req.symbol).await,
251        )
252    }
253
254    #[tool(
255        description = "Research rating summary/detail list. Futu API v10.6: OpenQuoteContext.get_research_rating_summary."
256    )]
257    async fn futu_get_research_rating_summary(
258        &self,
259        Parameters(req): Parameters<ResearchRatingSummaryReq>,
260        req_ctx: RequestContext<RoleServer>,
261    ) -> std::result::Result<String, String> {
262        req.validate()?;
263        tracing::info!(
264            tool = "futu_get_research_rating_summary",
265            symbol = %req.symbol,
266            rating_dimension_type = ?req.rating_dimension_type,
267            uid = ?req.uid,
268            next_key = ?req.next_key,
269            num = ?req.num
270        );
271        let client = self
272            .read_client_or_err("futu_get_research_rating_summary", &req_ctx, None, None)
273            .await?;
274        Self::wrap_result(
275            handlers::reference::get_research_rating_summary(
276                &client,
277                &req.symbol,
278                req.rating_dimension_type,
279                req.uid.as_deref(),
280                req.next_key.as_deref(),
281                req.num,
282            )
283            .await,
284        )
285    }
286
287    #[tool(
288        description = "Morningstar research report. Futu API v10.6: OpenQuoteContext.get_research_morningstar_report."
289    )]
290    async fn futu_get_research_morningstar_report(
291        &self,
292        Parameters(req): Parameters<ResearchMorningstarReportReq>,
293        req_ctx: RequestContext<RoleServer>,
294    ) -> std::result::Result<String, String> {
295        tracing::info!(
296            tool = "futu_get_research_morningstar_report",
297            symbol = %req.symbol
298        );
299        let client = self
300            .read_client_or_err("futu_get_research_morningstar_report", &req_ctx, None, None)
301            .await?;
302        Self::wrap_result(
303            handlers::reference::get_research_morningstar_report(&client, &req.symbol).await,
304        )
305    }
306
307    #[tool(
308        description = "Valuation detail for a stock or index. Futu API v10.6: OpenQuoteContext.get_valuation_detail."
309    )]
310    async fn futu_get_valuation_detail(
311        &self,
312        Parameters(req): Parameters<ValuationDetailReq>,
313        req_ctx: RequestContext<RoleServer>,
314    ) -> std::result::Result<String, String> {
315        req.validate()?;
316        tracing::info!(
317            tool = "futu_get_valuation_detail",
318            symbol = %req.symbol,
319            valuation_type = ?req.valuation_type,
320            interval_type = ?req.interval_type
321        );
322        let client = self
323            .read_client_or_err("futu_get_valuation_detail", &req_ctx, None, None)
324            .await?;
325        Self::wrap_result(
326            handlers::reference::get_valuation_detail(
327                &client,
328                &req.symbol,
329                req.valuation_type,
330                req.interval_type,
331            )
332            .await,
333        )
334    }
335
336    #[tool(
337        description = "Valuation stock list for a plate or index. Futu API v10.6: OpenQuoteContext.get_valuation_plate_stock_list."
338    )]
339    async fn futu_get_valuation_plate_stock_list(
340        &self,
341        Parameters(req): Parameters<ValuationPlateStockListReq>,
342        req_ctx: RequestContext<RoleServer>,
343    ) -> std::result::Result<String, String> {
344        req.validate()?;
345        tracing::info!(
346            tool = "futu_get_valuation_plate_stock_list",
347            symbol = %req.symbol,
348            valuation_type = ?req.valuation_type,
349            next_key = ?req.next_key,
350            num = ?req.num,
351            sort_type = ?req.sort_type,
352            sort_id = ?req.sort_id,
353            filter_security = ?req.filter_security
354        );
355        let client = self
356            .read_client_or_err("futu_get_valuation_plate_stock_list", &req_ctx, None, None)
357            .await?;
358        Self::wrap_result(
359            handlers::reference::get_valuation_plate_stock_list(
360                &client,
361                &req.symbol,
362                req.valuation_type,
363                req.next_key.as_deref(),
364                req.num,
365                req.sort_type,
366                req.sort_id,
367                req.filter_security.as_deref(),
368            )
369            .await,
370        )
371    }
372}