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