Skip to main content

futu_mcp/tool_args/qot/
basic.rs

1//! MCP QOT request schemas split from the tool-args root.
2
3use super::*;
4
5#[derive(Debug, Deserialize, schemars::JsonSchema)]
6#[serde(deny_unknown_fields)]
7pub struct SymbolReq {
8    #[schemars(
9        description = "Security symbol in MARKET.CODE format, e.g. HK.00700, US.AAPL \
10                              (alias: code / stock / security for SDK compat)"
11    )]
12    // v1.4.83 §5 Phase 3: 接受 code / stock / security 三种 alias (对齐
13    // py-futu-api 风格 + v1.4.82 A1/B1 REST adapter alias). 新代码推荐用
14    // `symbol` (Rust native snake_case).
15    #[serde(alias = "code", alias = "stock", alias = "security")]
16    pub symbol: String,
17}
18
19#[derive(Debug, Deserialize, schemars::JsonSchema)]
20#[serde(deny_unknown_fields)]
21pub struct ShortInfoReq {
22    #[schemars(
23        description = "Security symbol in MARKET.CODE format, e.g. HK.00700, US.AAPL \
24                       (alias: code / stock / security for SDK compat)"
25    )]
26    #[serde(alias = "code", alias = "stock", alias = "security")]
27    pub symbol: String,
28    #[schemars(description = "Pagination key; alias: nextKey")]
29    #[serde(default, alias = "nextKey")]
30    pub next_key: Option<String>,
31    #[schemars(description = "Page size, 1..50; omitted uses backend default 10")]
32    #[serde(default)]
33    pub num: Option<i32>,
34}
35
36impl ShortInfoReq {
37    pub fn validate(&self, tool: &str) -> Result<(), String> {
38        validate_optional_i32_range(tool, "num", self.num, 1, 50)
39    }
40}
41
42#[derive(Debug, Deserialize, schemars::JsonSchema)]
43#[serde(deny_unknown_fields)]
44pub struct TopTenBuySellBrokersReq {
45    #[schemars(
46        description = "HK security symbol in MARKET.CODE format, e.g. HK.00700 \
47                       (alias: code / stock / security for SDK compat)"
48    )]
49    #[serde(alias = "code", alias = "stock", alias = "security")]
50    pub symbol: String,
51    #[schemars(
52        description = "0 or omitted = realtime; N = previous N trading days; must be non-negative; alias: daysBefore"
53    )]
54    #[serde(default, alias = "daysBefore")]
55    pub days_before: Option<i32>,
56}
57
58impl TopTenBuySellBrokersReq {
59    pub fn validate(&self) -> Result<(), String> {
60        if self.days_before.unwrap_or(0) < 0 {
61            return Err(format!(
62                "days_before must be non-negative, got {}",
63                self.days_before.unwrap_or(0)
64            ));
65        }
66        Ok(())
67    }
68}
69
70#[derive(Debug, Deserialize, schemars::JsonSchema)]
71#[serde(deny_unknown_fields)]
72pub struct OptionVolatilityReq {
73    #[schemars(
74        description = "Option symbol in MARKET.CODE format, e.g. US.AAPL260116C200000 \
75                       (alias: code / option / security for SDK compat)"
76    )]
77    #[serde(alias = "code", alias = "option", alias = "security")]
78    pub symbol: String,
79    #[schemars(
80        description = "Query period: 0/omitted=month, 1=week, 2=month, 3=quarter, 4=half-year, 5=year"
81    )]
82    #[serde(default, alias = "queryTimePeriod")]
83    pub query_time_period: Option<i32>,
84    #[schemars(
85        description = "Underlying historical-volatility period in days, 5..250; omitted uses backend default 30"
86    )]
87    #[serde(default, alias = "hvTimePeriod")]
88    pub hv_time_period: Option<i32>,
89}
90
91impl OptionVolatilityReq {
92    pub fn validate(&self) -> Result<(), String> {
93        if let Some(query_time_period) = self.query_time_period
94            && query_time_period != 0
95            && !(1..=5).contains(&query_time_period)
96        {
97            return Err(format!(
98                "query_time_period must be 0 or in 1..=5 for futu_get_option_volatility, got {query_time_period}"
99            ));
100        }
101        validate_optional_i32_range(
102            "futu_get_option_volatility",
103            "hv_time_period",
104            self.hv_time_period,
105            5,
106            250,
107        )
108    }
109}
110
111#[derive(Debug, Deserialize, schemars::JsonSchema)]
112#[serde(deny_unknown_fields)]
113pub struct OptionExerciseProbabilityReq {
114    #[schemars(
115        description = "Option symbol in MARKET.CODE format, e.g. US.AAPL260116C200000 \
116                       (alias: code / option / security for SDK compat)"
117    )]
118    #[serde(alias = "code", alias = "option", alias = "security")]
119    pub symbol: String,
120}
121
122#[derive(Debug, Deserialize, schemars::JsonSchema)]
123#[serde(deny_unknown_fields)]
124pub struct SymbolListReq {
125    #[schemars(description = "Array of security symbols in MARKET.CODE format \
126                       (e.g. [\"HK.00700\", \"US.AAPL\"]). Field name is \
127                       `symbols` (Rust native snake_case); aliases: `stocks` \
128                       / `code_list` / `symbol_list` / `security_list` \
129                       for SDK compat.")]
130    // v1.4.83 §5 Phase 3: 接受 stocks / code_list / symbol_list /
131    // security_list 四种 alias (对齐 v1.4.82 B1 REST adapter alias).
132    #[serde(
133        alias = "stocks",
134        alias = "code_list",
135        alias = "symbol_list",
136        alias = "security_list"
137    )]
138    pub symbols: Vec<String>,
139}
140
141#[derive(Debug, Deserialize, schemars::JsonSchema)]
142#[serde(deny_unknown_fields)]
143pub struct KLineReq {
144    #[schemars(description = "Security symbol (MARKET.CODE); alias: code / stock")]
145    // v1.4.83 §5 Phase 3
146    #[serde(alias = "code", alias = "stock")]
147    pub symbol: String,
148    #[schemars(
149        description = "K-line type: day|week|month|quarter|year|1min|3min|5min|15min|30min|60min \
150                       (alias: ktype / k_type / kl_type for SDK compat)"
151    )]
152    #[serde(default = "default_kl_type", alias = "ktype", alias = "k_type")]
153    pub kl_type: String,
154    #[schemars(
155        description = "Number of candles to return (default 100); alias: num / max_count / req_count"
156    )]
157    #[serde(alias = "num", alias = "max_count", alias = "req_count")]
158    pub count: Option<i32>,
159    #[schemars(
160        description = "Start date yyyy-MM-dd (optional; default computed from count); alias: begin_time / from"
161    )]
162    #[serde(alias = "begin_time", alias = "from")]
163    pub begin: Option<String>,
164    #[schemars(
165        description = "End date yyyy-MM-dd (optional; default today); alias: end_time / to"
166    )]
167    #[serde(alias = "end_time", alias = "to")]
168    pub end: Option<String>,
169}
170
171impl KLineReq {
172    pub fn validate(&self) -> Result<(), String> {
173        if let Some(count) = self.count
174            && !(1..=1000).contains(&count)
175        {
176            return Err(format!(
177                "count must be in 1..=1000 for futu_get_kline, got {count}"
178            ));
179        }
180        Ok(())
181    }
182}
183
184#[derive(Debug, Deserialize, schemars::JsonSchema)]
185#[serde(deny_unknown_fields)]
186pub struct OrderBookReq {
187    #[schemars(description = "Security symbol (MARKET.CODE); alias: code / stock / security")]
188    // v1.4.84 §5 B1
189    #[serde(alias = "code", alias = "stock", alias = "security")]
190    pub symbol: String,
191    #[schemars(description = "Order book depth, 1-40 (default 10); alias: num")]
192    #[serde(default = "default_depth", alias = "num")]
193    pub depth: i32,
194    #[schemars(
195        description = "Set true to query SG/MY odd-lot orderbook (SubType_OrderBook_Odd=22)"
196    )]
197    #[serde(default, alias = "oddLot", alias = "is_odd_lot")]
198    pub odd_lot: bool,
199}
200
201impl OrderBookReq {
202    pub fn validate(&self) -> Result<(), String> {
203        if !(1..=40).contains(&self.depth) {
204            return Err(format!(
205                "depth must be in 1..=40 for futu_get_orderbook, got {}",
206                self.depth
207            ));
208        }
209        Ok(())
210    }
211}
212
213#[derive(Debug, Deserialize, schemars::JsonSchema)]
214#[serde(deny_unknown_fields)]
215pub struct TickerReq {
216    #[schemars(description = "Security symbol (MARKET.CODE); alias: code / stock / security")]
217    // v1.4.84 §5 B1
218    #[serde(alias = "code", alias = "stock", alias = "security")]
219    pub symbol: String,
220    #[schemars(
221        description = "Number of ticks to fetch (default 100, max 1000); alias: num / max_count / req_count"
222    )]
223    #[serde(
224        default = "default_ticker_count",
225        alias = "num",
226        alias = "max_count",
227        alias = "req_count"
228    )]
229    pub count: i32,
230}
231
232impl TickerReq {
233    pub fn validate(&self) -> Result<(), String> {
234        if !(1..=1000).contains(&self.count) {
235            return Err(format!(
236                "count must be in 1..=1000 for futu_get_ticker, got {}",
237                self.count
238            ));
239        }
240        Ok(())
241    }
242}
243
244#[derive(Debug, Deserialize, schemars::JsonSchema)]
245#[serde(deny_unknown_fields)]
246pub struct PlateListReq {
247    #[schemars(description = "Market: HK|HK_FUTURE|US|SH|SZ")]
248    pub market: String,
249    #[schemars(
250        description = "Plate set: all|industry|region|concept (default all); alias: plate_set_type. \
251                       region only has backend data for SH/SZ; HK/HK_FUTURE/US returns an empty successful list like the native gateway."
252    )]
253    // v1.4.84 §5 B1
254    #[serde(default = "default_plate_set", alias = "plate_set_type")]
255    pub plate_set: String,
256}
257
258#[derive(Debug, Deserialize, schemars::JsonSchema)]
259#[serde(deny_unknown_fields)]
260pub struct PlateStocksReq {
261    #[schemars(
262        description = "Plate symbol, MARKET.CODE format (e.g. HK.LIST1001); alias: symbol / code / plate_code"
263    )]
264    // v1.4.84 §5 B1
265    #[serde(alias = "symbol", alias = "code", alias = "plate_code")]
266    pub plate: String,
267}
268
269#[derive(Debug, Deserialize, schemars::JsonSchema)]
270#[serde(deny_unknown_fields)]
271pub struct MarketStateReq {
272    #[schemars(
273        description = "Symbols list in MARKET.CODE format; alias: stocks / code_list / symbol_list / security_list"
274    )]
275    // v1.4.84 §5 B1
276    #[serde(
277        alias = "stocks",
278        alias = "code_list",
279        alias = "symbol_list",
280        alias = "security_list"
281    )]
282    pub symbols: Vec<String>,
283}