1use crate::handlers;
4use crate::tool_args::{
5 KLineReq, OrderBookReq, PlateListReq, PlateStocksReq, SymbolListReq, SymbolReq, TickerReq,
6};
7use rmcp::{
8 RoleServer, handler::server::wrapper::Parameters, service::RequestContext, tool, tool_router,
9};
10
11use super::FutuServer;
12
13#[tool_router(router = market_tool_router, vis = "pub(crate)")]
14impl FutuServer {
15 #[tool(
16 description = "Get real-time basic quote (price, volume, turnover) for a security. Auto-subscribes SubType::Basic on first call."
17 )]
18 async fn futu_get_quote(
19 &self,
20 Parameters(req): Parameters<SymbolReq>,
21 req_ctx: RequestContext<RoleServer>,
22 ) -> std::result::Result<String, String> {
23 tracing::info!(tool = "futu_get_quote", symbol = %req.symbol);
24 let client = self
25 .read_client_or_err("futu_get_quote", &req_ctx, None, None)
26 .await?;
27 Self::wrap_result(handlers::core::get_quote(&client, &req.symbol).await)
28 }
29
30 #[tool(
31 description = "Get a security snapshot (one-shot, no subscription) with extended fields: 52-week high/low, avg price, volume ratio, amplitude, bid/ask."
32 )]
33 async fn futu_get_snapshot(
34 &self,
35 Parameters(req): Parameters<SymbolReq>,
36 req_ctx: RequestContext<RoleServer>,
37 ) -> std::result::Result<String, String> {
38 tracing::info!(tool = "futu_get_snapshot", symbol = %req.symbol);
39 let client = self
40 .read_client_or_err("futu_get_snapshot", &req_ctx, None, None)
41 .await?;
42 Self::wrap_result(handlers::core::get_snapshot(&client, &req.symbol).await)
43 }
44
45 #[tool(
46 description = "Get historical K-line (OHLCV). Supports day/week/month/quarter/year plus 1/3/5/15/30/60 minute bars."
47 )]
48 async fn futu_get_kline(
49 &self,
50 Parameters(req): Parameters<KLineReq>,
51 req_ctx: RequestContext<RoleServer>,
52 ) -> std::result::Result<String, String> {
53 tracing::info!(
54 tool = "futu_get_kline",
55 symbol = %req.symbol,
56 kl_type = %req.kl_type,
57 count = crate::state::audit_fmt::opt_i32(req.count)
59 );
60 let client = self
61 .read_client_or_err("futu_get_kline", &req_ctx, None, None)
62 .await?;
63 Self::wrap_result(
64 handlers::market::get_kline(
65 &client,
66 &req.symbol,
67 &req.kl_type,
68 req.count,
69 req.begin.as_deref(),
70 req.end.as_deref(),
71 )
72 .await,
73 )
74 }
75
76 #[tool(
77 description = "Get the order book (bids and asks with price, volume, order count). Auto-subscribes OrderBook."
78 )]
79 async fn futu_get_orderbook(
80 &self,
81 Parameters(req): Parameters<OrderBookReq>,
82 req_ctx: RequestContext<RoleServer>,
83 ) -> std::result::Result<String, String> {
84 tracing::info!(tool = "futu_get_orderbook", symbol = %req.symbol, depth = req.depth);
85 let client = self
86 .read_client_or_err("futu_get_orderbook", &req_ctx, None, None)
87 .await?;
88 Self::wrap_result(handlers::market::get_orderbook(&client, &req.symbol, req.depth).await)
89 }
90
91 #[tool(description = "Get recent ticker (trade-by-trade). Auto-subscribes Ticker.")]
92 async fn futu_get_ticker(
93 &self,
94 Parameters(req): Parameters<TickerReq>,
95 req_ctx: RequestContext<RoleServer>,
96 ) -> std::result::Result<String, String> {
97 tracing::info!(tool = "futu_get_ticker", symbol = %req.symbol, count = req.count);
98 let client = self
99 .read_client_or_err("futu_get_ticker", &req_ctx, None, None)
100 .await?;
101 Self::wrap_result(handlers::market::get_ticker(&client, &req.symbol, req.count).await)
102 }
103
104 #[tool(
105 description = "Get intraday (RT / time-sharing) minute-by-minute price series. Auto-subscribes RT."
106 )]
107 async fn futu_get_rt(
108 &self,
109 Parameters(req): Parameters<SymbolReq>,
110 req_ctx: RequestContext<RoleServer>,
111 ) -> std::result::Result<String, String> {
112 tracing::info!(tool = "futu_get_rt", symbol = %req.symbol);
113 let client = self
114 .read_client_or_err("futu_get_rt", &req_ctx, None, None)
115 .await?;
116 Self::wrap_result(handlers::market::get_rt(&client, &req.symbol).await)
117 }
118
119 #[tool(
120 description = "Get static info (name, lot size, listing date) for one or more securities. No subscription needed."
121 )]
122 async fn futu_get_static(
123 &self,
124 Parameters(req): Parameters<SymbolListReq>,
125 req_ctx: RequestContext<RoleServer>,
126 ) -> std::result::Result<String, String> {
127 tracing::info!(tool = "futu_get_static", symbols = ?req.symbols);
128 let client = self
129 .read_client_or_err("futu_get_static", &req_ctx, None, None)
130 .await?;
131 Self::wrap_result(handlers::market::get_static(&client, &req.symbols).await)
132 }
133
134 #[tool(description = "Get the broker queue (HK only). Auto-subscribes Broker.")]
135 async fn futu_get_broker(
136 &self,
137 Parameters(req): Parameters<SymbolReq>,
138 req_ctx: RequestContext<RoleServer>,
139 ) -> std::result::Result<String, String> {
140 tracing::info!(tool = "futu_get_broker", symbol = %req.symbol);
141 let client = self
142 .read_client_or_err("futu_get_broker", &req_ctx, None, None)
143 .await?;
144 Self::wrap_result(handlers::market::get_broker(&client, &req.symbol).await)
145 }
146
147 #[tool(description = "List plates by market and set type (industry / region / concept / all).")]
148 async fn futu_list_plates(
149 &self,
150 Parameters(req): Parameters<PlateListReq>,
151 req_ctx: RequestContext<RoleServer>,
152 ) -> std::result::Result<String, String> {
153 tracing::info!(tool = "futu_list_plates", market = %req.market, set = %req.plate_set);
154 let client = self
155 .read_client_or_err("futu_list_plates", &req_ctx, None, None)
156 .await?;
157 Self::wrap_result(handlers::plate::list_plates(&client, &req.market, &req.plate_set).await)
158 }
159
160 #[tool(description = "List constituent securities of a plate.")]
161 async fn futu_plate_stocks(
162 &self,
163 Parameters(req): Parameters<PlateStocksReq>,
164 req_ctx: RequestContext<RoleServer>,
165 ) -> std::result::Result<String, String> {
166 tracing::info!(tool = "futu_plate_stocks", plate = %req.plate);
167 let client = self
168 .read_client_or_err("futu_plate_stocks", &req_ctx, None, None)
169 .await?;
170 Self::wrap_result(handlers::plate::plate_stocks(&client, &req.plate).await)
171 }
172}