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 req.validate()?;
54 tracing::info!(
55 tool = "futu_get_kline",
56 symbol = %req.symbol,
57 kl_type = %req.kl_type,
58 count = crate::state::audit_fmt::opt_i32(req.count)
60 );
61 let client = self
62 .read_client_or_err("futu_get_kline", &req_ctx, None, None)
63 .await?;
64 Self::wrap_result(
65 handlers::market::get_kline(
66 &client,
67 &req.symbol,
68 &req.kl_type,
69 req.count,
70 req.begin.as_deref(),
71 req.end.as_deref(),
72 )
73 .await,
74 )
75 }
76
77 #[tool(
78 description = "Get the order book (bids and asks with price, volume, order count). Auto-subscribes OrderBook; set odd_lot=true for SG/MY odd-lot orderbook."
79 )]
80 async fn futu_get_orderbook(
81 &self,
82 Parameters(req): Parameters<OrderBookReq>,
83 req_ctx: RequestContext<RoleServer>,
84 ) -> std::result::Result<String, String> {
85 req.validate()?;
86 tracing::info!(
87 tool = "futu_get_orderbook",
88 symbol = %req.symbol,
89 depth = req.depth,
90 odd_lot = req.odd_lot
91 );
92 let client = self
93 .read_client_or_err("futu_get_orderbook", &req_ctx, None, None)
94 .await?;
95 Self::wrap_result(
96 handlers::market::get_orderbook(&client, &req.symbol, req.depth, req.odd_lot).await,
97 )
98 }
99
100 #[tool(description = "Get recent ticker (trade-by-trade). Auto-subscribes Ticker.")]
101 async fn futu_get_ticker(
102 &self,
103 Parameters(req): Parameters<TickerReq>,
104 req_ctx: RequestContext<RoleServer>,
105 ) -> std::result::Result<String, String> {
106 req.validate()?;
107 tracing::info!(tool = "futu_get_ticker", symbol = %req.symbol, count = req.count);
108 let client = self
109 .read_client_or_err("futu_get_ticker", &req_ctx, None, None)
110 .await?;
111 Self::wrap_result(handlers::market::get_ticker(&client, &req.symbol, req.count).await)
112 }
113
114 #[tool(
115 description = "Get intraday (RT / time-sharing) minute-by-minute price series. Auto-subscribes RT."
116 )]
117 async fn futu_get_rt(
118 &self,
119 Parameters(req): Parameters<SymbolReq>,
120 req_ctx: RequestContext<RoleServer>,
121 ) -> std::result::Result<String, String> {
122 tracing::info!(tool = "futu_get_rt", symbol = %req.symbol);
123 let client = self
124 .read_client_or_err("futu_get_rt", &req_ctx, None, None)
125 .await?;
126 Self::wrap_result(handlers::market::get_rt(&client, &req.symbol).await)
127 }
128
129 #[tool(
130 description = "Get static info (name, lot size, listing date) for one or more securities. No subscription needed."
131 )]
132 async fn futu_get_static(
133 &self,
134 Parameters(req): Parameters<SymbolListReq>,
135 req_ctx: RequestContext<RoleServer>,
136 ) -> std::result::Result<String, String> {
137 tracing::info!(tool = "futu_get_static", symbols = ?req.symbols);
138 let client = self
139 .read_client_or_err("futu_get_static", &req_ctx, None, None)
140 .await?;
141 Self::wrap_result(handlers::market::get_static(&client, &req.symbols).await)
142 }
143
144 #[tool(description = "Get the broker queue (HK only). Auto-subscribes Broker.")]
145 async fn futu_get_broker(
146 &self,
147 Parameters(req): Parameters<SymbolReq>,
148 req_ctx: RequestContext<RoleServer>,
149 ) -> std::result::Result<String, String> {
150 tracing::info!(tool = "futu_get_broker", symbol = %req.symbol);
151 let client = self
152 .read_client_or_err("futu_get_broker", &req_ctx, None, None)
153 .await?;
154 Self::wrap_result(handlers::market::get_broker(&client, &req.symbol).await)
155 }
156
157 #[tool(description = "List plates by market and set type (industry / region / concept / all).")]
158 async fn futu_list_plates(
159 &self,
160 Parameters(req): Parameters<PlateListReq>,
161 req_ctx: RequestContext<RoleServer>,
162 ) -> std::result::Result<String, String> {
163 tracing::info!(tool = "futu_list_plates", market = %req.market, set = %req.plate_set);
164 let client = self
165 .read_client_or_err("futu_list_plates", &req_ctx, None, None)
166 .await?;
167 Self::wrap_result(handlers::plate::list_plates(&client, &req.market, &req.plate_set).await)
168 }
169
170 #[tool(description = "List constituent securities of a plate.")]
171 async fn futu_plate_stocks(
172 &self,
173 Parameters(req): Parameters<PlateStocksReq>,
174 req_ctx: RequestContext<RoleServer>,
175 ) -> std::result::Result<String, String> {
176 tracing::info!(tool = "futu_plate_stocks", plate = %req.plate);
177 let client = self
178 .read_client_or_err("futu_plate_stocks", &req_ctx, None, None)
179 .await?;
180 Self::wrap_result(handlers::plate::plate_stocks(&client, &req.plate).await)
181 }
182}