1use super::*;
4
5#[derive(Debug, Deserialize, schemars::JsonSchema)]
6#[serde(deny_unknown_fields)]
7pub struct UserSecurityGroupReq {
8 #[schemars(description = "Group type: 1=custom, 2=system, 3=all (default 1)")]
9 #[serde(default = "default_user_security_group_type")]
10 pub group_type: i32,
11}
12
13impl UserSecurityGroupReq {
14 pub fn validate(&self) -> Result<(), String> {
15 if (1..=3).contains(&self.group_type) {
16 return Ok(());
17 }
18 Err(format!(
19 "group_type must be in 1..=3 (1=custom, 2=system, 3=all), got {}",
20 self.group_type
21 ))
22 }
23}
24
25#[derive(Debug, Deserialize, schemars::JsonSchema)]
26#[serde(deny_unknown_fields)]
27pub struct StockFilterReq {
28 #[schemars(
29 description = "Market code accepted by the StockFilter backend: int 1=HK, 2=HK_FUTURE, 11=US, 21=SH/CN, 22=SZ, 31=SG, 41=JP, 61=MY; \
30 or string HK/HK_FUTURE/US/SH/SZ/CN/SG/JP/MY."
31 )]
32 #[serde(deserialize_with = "deser_stock_filter_market_as_i32")]
34 pub market: i32,
35 #[schemars(description = "Pagination begin index (default 0); alias: offset / skip")]
36 #[serde(default, alias = "offset", alias = "skip")]
38 pub begin: i32,
39 #[schemars(description = "Max rows (0-200, default 50); alias: count / max_count / req_count")]
40 #[serde(
41 default = "default_stock_filter_num",
42 alias = "count",
43 alias = "max_count",
44 alias = "req_count"
45 )]
46 pub num: i32,
47}
48
49impl StockFilterReq {
50 pub fn validate(&self) -> Result<(), String> {
51 if !futu_core::qot_endpoint_market::is_stock_filter_market(self.market) {
52 return Err(format!(
53 "futu_get_stock_filter market must be {}, got {}",
54 futu_core::qot_endpoint_market::QOT_STOCK_FILTER_MARKET_VALID_VALUES,
55 self.market,
56 ));
57 }
58 futu_core::qot_page_bounds::validate_begin_num(self.begin, self.num, 200, "stock_filter")
59 .map(|_| ())
60 .map_err(|err| err.to_string())
61 }
62}
63
64#[derive(Debug, Deserialize, schemars::JsonSchema)]
65#[serde(deny_unknown_fields)]
66pub struct TradingDaysReq {
67 #[schemars(
68 description = "Market code — **Qot_Common.TradeDateMarket enum** (i32, NOT QotMarket!): \
69 1=HK, 2=US, 3=CN, 4=NorthboundSZ/SH, 5=SouthboundHK, \
70 6=JP_Future, 7=SG_Future, 8=SG, 9=MY, 10=JP. \
71 Different from QotMarket (ipo_list/stock_filter use 1=HK 2=HK_FUTURE 11=US 21=SH 22=SZ). \
72 Legacy QotMarket aliases 11=US, 21=SH, 22=SZ are accepted for backward compatibility."
73 )]
74 pub market: i32,
75 #[schemars(description = "Begin date (yyyy-MM-dd); alias: begin / start_time / from")]
76 #[serde(alias = "begin", alias = "start_time", alias = "from")]
78 pub begin_time: String,
79 #[schemars(description = "End date (yyyy-MM-dd); alias: end / to")]
80 #[serde(alias = "end", alias = "to")]
81 pub end_time: String,
82}
83
84impl TradingDaysReq {
85 pub fn validate(&self) -> Result<(), String> {
86 if futu_core::qot_trade_date_market::is_trade_date_market(self.market) {
87 return Ok(());
88 }
89 Err(format!(
90 "futu_get_trading_days market must be {}, got {}",
91 futu_core::qot_trade_date_market::TRADE_DATE_MARKET_VALID_VALUES,
92 self.market
93 ))
94 }
95}
96
97#[derive(Debug, Deserialize, schemars::JsonSchema)]
98#[serde(deny_unknown_fields)]
99pub struct SuspendReq {
100 #[schemars(description = "Array of security symbols in MARKET.CODE format \
101 (e.g. [\"HK.00700\", \"HK.09988\"]). Alias: stocks / code_list / symbol_list / security_list")]
102 #[serde(
104 alias = "stocks",
105 alias = "code_list",
106 alias = "symbol_list",
107 alias = "security_list"
108 )]
109 pub symbols: Vec<String>,
110 #[schemars(description = "Begin date (yyyy-MM-dd); alias: begin / start_time / from")]
111 #[serde(alias = "begin", alias = "start_time", alias = "from")]
112 pub begin_time: String,
113 #[schemars(description = "End date (yyyy-MM-dd); alias: end / to")]
114 #[serde(alias = "end", alias = "to")]
115 pub end_time: String,
116}
117
118#[derive(Debug, Deserialize, schemars::JsonSchema)]
119#[serde(deny_unknown_fields)]
120pub struct UserSecurityReq {
121 #[schemars(
122 description = "Watchlist group name (use futu_get_user_security_group to list groups); alias: group / name"
123 )]
124 #[serde(alias = "group", alias = "name")]
126 pub group_name: String,
127}
128
129#[derive(Debug, Deserialize, schemars::JsonSchema)]
130#[serde(deny_unknown_fields)]
131pub struct HistoryKlQuotaReq {
132 #[schemars(
133 description = "Whether to fetch detailed per-symbol download history (default false)"
134 )]
135 #[serde(default)]
136 pub get_detail: bool,
137}
138
139#[derive(Debug, Deserialize, schemars::JsonSchema)]
140#[serde(deny_unknown_fields)]
141pub struct HoldingChangeReq {
142 #[schemars(
143 description = "Underlying stock symbol (e.g. HK.00700, US.AAPL); alias: code / stock"
144 )]
145 #[serde(alias = "code", alias = "stock")]
147 pub symbol: String,
148 #[schemars(
149 description = "Holder category: 1=Institution, 2=Fund, 3=Executive; alias: category"
150 )]
151 #[serde(alias = "category")]
152 pub holder_category: i32,
153 #[schemars(
154 description = "Begin time YYYY-MM-DD HH:MM:SS (optional); alias: begin / start_time / from"
155 )]
156 #[serde(default, alias = "begin", alias = "start_time", alias = "from")]
157 pub begin_time: Option<String>,
158 #[schemars(description = "End time YYYY-MM-DD HH:MM:SS (optional); alias: end / to")]
159 #[serde(default, alias = "end", alias = "to")]
160 pub end_time: Option<String>,
161}
162
163#[derive(Debug, Deserialize, schemars::JsonSchema)]
164#[serde(deny_unknown_fields)]
165pub struct ModifyUserSecurityReq {
166 #[schemars(description = "Watchlist group name; alias: group / name")]
167 #[serde(alias = "group", alias = "name")]
169 pub group_name: String,
170 #[schemars(
171 description = "Op: 1=AddInto, 2=Delete (from this group), 3=MoveOut; alias: op_type / operation"
172 )]
173 #[serde(alias = "op_type", alias = "operation")]
174 pub op: i32,
175 #[schemars(
176 description = "Security symbols to add/delete/move; alias: stocks / code_list / symbol_list / security_list"
177 )]
178 #[serde(
179 alias = "stocks",
180 alias = "code_list",
181 alias = "symbol_list",
182 alias = "security_list"
183 )]
184 pub symbols: Vec<String>,
185}
186
187impl ModifyUserSecurityReq {
188 pub fn validate(&self) -> Result<(), String> {
189 if (1..=3).contains(&self.op) {
190 return Ok(());
191 }
192 Err(format!(
193 "op must be in 1..=3 (1=add, 2=delete, 3=move out), got {}",
194 self.op
195 ))
196 }
197}
198
199#[derive(Debug, Deserialize, schemars::JsonSchema)]
200#[serde(deny_unknown_fields)]
201pub struct CodeChangeReq {
202 #[schemars(
203 description = "Security symbols to query (currently HK only); alias: stocks / code_list / symbol_list / security_list"
204 )]
205 #[serde(
207 alias = "stocks",
208 alias = "code_list",
209 alias = "symbol_list",
210 alias = "security_list"
211 )]
212 pub symbols: Vec<String>,
213}
214
215#[derive(Debug, Deserialize, schemars::JsonSchema)]
216#[serde(deny_unknown_fields)]
217pub struct BizGroupReq {
218 #[schemars(description = "Trade env: real / simulate (default real)")]
219 #[serde(default = "default_env", alias = "trd_env")]
220 pub env: String,
221 #[schemars(description = "Trading account ID (u64)")]
222 pub acc_id: u64,
223 #[schemars(
224 description = "Optional legacy market hint; accepted for backward compatibility but ignored. Daemon derives backend market from acc_id/account cache."
225 )]
226 #[serde(
227 default,
228 deserialize_with = "tool_enums::deser_trd_market_as_option_string"
229 )]
230 pub market: Option<String>,
231}
232
233#[derive(Debug, Deserialize, schemars::JsonSchema)]
234#[serde(deny_unknown_fields)]
235pub struct BondSymbolReq {
236 #[schemars(description = "Trade env: real / simulate (default real)")]
237 #[serde(default = "default_env", alias = "trd_env")]
238 pub env: String,
239 #[schemars(description = "Trading account ID (u64) for per-broker routing")]
240 pub acc_id: u64,
241 #[schemars(description = "Market: HK / US / SG; aliases USA and SG_UNIVERSAL are accepted")]
242 pub market: String,
243 #[schemars(description = "Bond symbol (债券代码, 如 HK1234 或 11000018)")]
244 pub symbol: String,
245}
246
247#[derive(Debug, Deserialize, schemars::JsonSchema)]
249#[serde(deny_unknown_fields)]
250pub struct TickerStatisticReq {
251 #[schemars(description = "Security symbol in MARKET.CODE format, e.g. HK.00700, US.AAPL")]
252 #[serde(alias = "code", alias = "stock", alias = "security")]
253 pub symbol: String,
254 #[schemars(
255 description = "Ticker type filter: 0=ALL, 1=BUY, 2=SELL, 3=BUY_AND_SELL, 4=NEUTRAL (default ALL)"
256 )]
257 #[serde(default)]
258 pub ticker_type: Option<i32>,
259 #[schemars(description = "Market session: 0=ALL, 1=BEFORE, 2=TRADING, 3=AFTER (default ALL)")]
260 #[serde(default)]
261 pub stat_type: Option<u32>,
262}
263
264#[derive(Debug, Deserialize, schemars::JsonSchema)]
267#[serde(deny_unknown_fields)]
268pub struct TickerStatisticDetailReq {
269 #[schemars(description = "Security symbol in MARKET.CODE format, e.g. HK.00700, US.AAPL")]
270 #[serde(alias = "code", alias = "stock", alias = "security")]
271 pub symbol: String,
272 #[schemars(
273 description = "Ticker type filter: 0=ALL, 1=BUY, 2=SELL, 3=BUY_AND_SELL, 4=NEUTRAL (default ALL)"
274 )]
275 #[serde(default)]
276 pub ticker_type: Option<i32>,
277 #[schemars(
278 description = "Ticker timestamp (ms) — usually from prior futu_get_ticker_statistic call. \
279 0 / omit = use backend latest available."
280 )]
281 #[serde(default)]
282 pub ticker_time: Option<u64>,
283 #[schemars(
284 description = "Filter type: 0=all price levels, 1..N=top N levels (backend max ~100)"
285 )]
286 #[serde(default)]
287 pub select_num: Option<u32>,
288 #[schemars(description = "Pagination start offset (default 0)")]
289 #[serde(default)]
290 pub data_from: Option<u32>,
291 #[schemars(description = "Pagination size, max items returned; if provided, must be positive")]
292 #[serde(default)]
293 pub data_max_count: Option<u32>,
294 #[schemars(description = "Market session: 0=ALL, 1=BEFORE, 2=TRADING, 3=AFTER (default ALL)")]
295 #[serde(default)]
296 pub stat_type: Option<u32>,
297}
298
299impl TickerStatisticDetailReq {
300 pub fn validate(&self) -> Result<(), String> {
301 if self.data_max_count == Some(0) {
302 return Err("data_max_count must be positive when provided".to_string());
303 }
304 Ok(())
305 }
306}
307
308#[derive(Debug, Deserialize, schemars::JsonSchema)]
309#[serde(deny_unknown_fields)]
310pub struct QuoteRightsReq {
311 #[schemars(description = "If true, trigger request_highest_quote_right before querying")]
312 #[serde(default)]
313 pub refresh: Option<bool>,
314}