Skip to main content

futucli/cmd/
proto_json.rs

1//! Proto-JSON passthrough helpers for newly added official endpoints.
2//!
3//! These commands intentionally accept generated `C2S` JSON instead of a large
4//! hand-written flag surface. That keeps v10.7 combo-option CLI coverage tied to
5//! the authoritative proto shape while the typed ergonomic CLI can evolve later.
6//!
7//! v1.8.0 large-file split(纯搬移, 零行为变化): 本文件保留 `qot_proto_json_command!`
8//! 宏与 qot 侧命令表(`scripts/tests/qot_3401_plus_api_guard.sh` 以 CLI 子命令字面量
9//! 扫本文件), 其余按职责放在 `proto_json/` 子模块, 对外路径经 `pub use` 原样回导出:
10//! - `contract.rs` — c2s JSON 解析 / surface-spec 合同校验 / combo 必填字段与写市场校验
11//! - `transport.rs` — 网关 round-trip / ret_type 检查 / 响应输出
12//! - `verification.rs` — `verification` 子命令
13//! - `trd_commands.rs` — trd 侧宏生成命令表
14//! - `combo.rs` — combo 下单 / combo max qty(含 PacketID 派生)
15
16use anyhow::Result;
17
18use crate::output::OutputFormat;
19
20mod combo;
21mod contract;
22mod transport;
23mod verification;
24
25#[cfg(test)]
26use combo::{ensure_combo_order_confirmed, packet_id_for_idempotency_key};
27pub use combo::{run_combo_max_trd_qtys, run_place_combo_order};
28use contract::parse_c2s;
29#[cfg(test)]
30use contract::{parse_combo_max_c2s_json, parse_place_combo_c2s_json};
31use transport::{ensure_success, print_proto_response, send_proto};
32pub use verification::run_verification;
33#[cfg(test)]
34use verification::validate_verification_args;
35
36macro_rules! qot_proto_json_command {
37    ($fn_name:ident, $label:literal, $proto_id:expr, $module:ident) => {
38        pub async fn $fn_name(gateway: &str, c2s_json: &str, output: OutputFormat) -> Result<()> {
39            let c2s = parse_c2s::<futu_proto::$module::C2s>($label, c2s_json)?;
40            let request = futu_proto::$module::Request { c2s };
41            let response: futu_proto::$module::Response =
42                send_proto(gateway, concat!("futucli-", $label), $proto_id, request).await?;
43            ensure_success(
44                $label,
45                response.ret_type,
46                response.ret_msg.as_deref(),
47                response.err_code,
48            )?;
49            print_proto_response(output, &response)
50        }
51    };
52}
53
54// trd 侧命令表复用同一宏, 必须在宏定义之后声明 (macro_rules! 文本作用域).
55mod trd_commands;
56pub use trd_commands::*;
57
58qot_proto_json_command!(
59    run_option_quote,
60    "option-quote",
61    futu_core::proto_id::QOT_GET_OPTION_QUOTE,
62    qot_get_option_quote
63);
64qot_proto_json_command!(
65    run_option_strategy,
66    "option-strategy",
67    futu_core::proto_id::QOT_GET_OPTION_STRATEGY,
68    qot_get_option_strategy
69);
70qot_proto_json_command!(
71    run_option_strategy_analysis,
72    "option-strategy-analysis",
73    futu_core::proto_id::QOT_GET_OPTION_STRATEGY_ANALYSIS,
74    qot_get_option_strategy_analysis
75);
76qot_proto_json_command!(
77    run_option_strategy_spread,
78    "option-strategy-spread",
79    futu_core::proto_id::QOT_GET_OPTION_STRATEGY_SPREAD,
80    qot_get_option_strategy_spread
81);
82qot_proto_json_command!(
83    run_earnings_calendar,
84    "earnings-calendar",
85    futu_core::proto_id::QOT_GET_EARNINGS_CALENDAR,
86    qot_get_earnings_calendar
87);
88qot_proto_json_command!(
89    run_macro_indicator_list,
90    "macro-indicator-list",
91    futu_core::proto_id::QOT_GET_MACRO_INDICATOR_LIST,
92    qot_get_macro_indicator_list
93);
94qot_proto_json_command!(
95    run_security_trading_sessions,
96    "security-trading-sessions",
97    futu_core::proto_id::QOT_GET_SECURITY_TRADING_SESSIONS,
98    qot_get_security_trading_sessions
99);
100qot_proto_json_command!(
101    run_market_trading_sessions,
102    "market-trading-sessions",
103    futu_core::proto_id::QOT_GET_MARKET_TRADING_SESSIONS,
104    qot_get_market_trading_sessions
105);
106qot_proto_json_command!(
107    run_kline_pattern,
108    "kline-pattern",
109    futu_core::proto_id::QOT_GET_KLINE_PATTERN,
110    qot_get_kline_pattern
111);
112qot_proto_json_command!(
113    run_kline_pattern_statistics,
114    "kline-pattern-statistics",
115    futu_core::proto_id::QOT_GET_KLINE_PATTERN_STATISTICS,
116    qot_get_kline_pattern_statistics
117);
118qot_proto_json_command!(
119    run_kline_pattern_stocks,
120    "kline-pattern-stocks",
121    futu_core::proto_id::QOT_GET_KLINE_PATTERN_STOCKS,
122    qot_get_kline_pattern_stocks
123);
124qot_proto_json_command!(
125    run_kline_pattern_performance,
126    "kline-pattern-performance",
127    futu_core::proto_id::QOT_GET_KLINE_PATTERN_PERFORMANCE,
128    qot_get_kline_pattern_performance
129);
130qot_proto_json_command!(
131    run_kline_pattern_catalog,
132    "kline-pattern-catalog",
133    futu_core::proto_id::QOT_GET_KLINE_PATTERN_CATALOG,
134    qot_get_kline_pattern_catalog
135);
136qot_proto_json_command!(
137    run_hot_news,
138    "hot-news",
139    futu_core::proto_id::QOT_GET_HOT_NEWS,
140    qot_get_hot_news
141);
142qot_proto_json_command!(
143    run_latest_news,
144    "latest-news",
145    futu_core::proto_id::QOT_GET_LATEST_NEWS,
146    qot_get_latest_news
147);
148qot_proto_json_command!(
149    run_watchlist_news,
150    "watchlist-news",
151    futu_core::proto_id::QOT_GET_WATCHLIST_NEWS,
152    qot_get_watchlist_news
153);
154qot_proto_json_command!(
155    run_watchlist_announcement,
156    "watchlist-announcement",
157    futu_core::proto_id::QOT_GET_WATCHLIST_ANNOUNCEMENT,
158    qot_get_watchlist_announcement
159);
160qot_proto_json_command!(
161    run_watchlist_rating,
162    "watchlist-rating",
163    futu_core::proto_id::QOT_GET_WATCHLIST_RATING,
164    qot_get_watchlist_rating
165);
166qot_proto_json_command!(
167    run_stock_news,
168    "stock-news",
169    futu_core::proto_id::QOT_GET_STOCK_NEWS,
170    qot_get_stock_news
171);
172qot_proto_json_command!(
173    run_etf_screen,
174    "etf-screen",
175    futu_core::proto_id::QOT_ETF_SCREEN,
176    qot_get_etf_screen
177);
178qot_proto_json_command!(
179    run_fund_screen,
180    "fund-screen",
181    futu_core::proto_id::QOT_FUND_SCREEN,
182    qot_get_fund_screen
183);
184qot_proto_json_command!(
185    run_future_screen,
186    "future-screen",
187    futu_core::proto_id::QOT_FUTURE_SCREEN,
188    qot_get_future_screen
189);
190qot_proto_json_command!(
191    run_bond_screen,
192    "bond-screen",
193    futu_core::proto_id::QOT_BOND_SCREEN,
194    qot_get_bond_screen
195);
196qot_proto_json_command!(
197    run_macro_indicator_history,
198    "macro-indicator-history",
199    futu_core::proto_id::QOT_GET_MACRO_INDICATOR_HISTORY,
200    qot_get_macro_indicator_history
201);
202qot_proto_json_command!(
203    run_fed_watch_target_rate,
204    "fed-watch-target-rate",
205    futu_core::proto_id::QOT_GET_FED_WATCH_TARGET_RATE,
206    qot_get_fed_watch_target_rate
207);
208qot_proto_json_command!(
209    run_fed_watch_dot_plot,
210    "fed-watch-dot-plot",
211    futu_core::proto_id::QOT_GET_FED_WATCH_DOT_PLOT,
212    qot_get_fed_watch_dot_plot
213);
214qot_proto_json_command!(
215    run_earnings_beat_rank,
216    "earnings-beat-rank",
217    futu_core::proto_id::QOT_GET_EARNINGS_BEAT_RANK,
218    qot_get_earnings_beat_rank
219);
220qot_proto_json_command!(
221    run_dividend_rank,
222    "dividend-rank",
223    futu_core::proto_id::QOT_GET_DIVIDEND_RANK,
224    qot_get_dividend_rank
225);
226qot_proto_json_command!(
227    run_dividend_calendar,
228    "dividend-calendar",
229    futu_core::proto_id::QOT_GET_DIVIDEND_CALENDAR,
230    qot_get_dividend_calendar
231);
232qot_proto_json_command!(
233    run_economic_calendar,
234    "economic-calendar",
235    futu_core::proto_id::QOT_GET_ECONOMIC_CALENDAR,
236    qot_get_economic_calendar
237);
238qot_proto_json_command!(
239    run_us_pre_market_rank,
240    "us-pre-market-rank",
241    futu_core::proto_id::QOT_GET_US_PRE_MARKET_RANK,
242    qot_get_us_pre_market_rank
243);
244qot_proto_json_command!(
245    run_us_after_hours_rank,
246    "us-after-hours-rank",
247    futu_core::proto_id::QOT_GET_US_AFTER_HOURS_RANK,
248    qot_get_us_after_hours_rank
249);
250qot_proto_json_command!(
251    run_us_overnight_rank,
252    "us-overnight-rank",
253    futu_core::proto_id::QOT_GET_US_OVERNIGHT_RANK,
254    qot_get_us_overnight_rank
255);
256qot_proto_json_command!(
257    run_top_movers_rank,
258    "top-movers-rank",
259    futu_core::proto_id::QOT_GET_TOP_MOVERS_RANK,
260    qot_get_top_movers_rank
261);
262qot_proto_json_command!(
263    run_hot_list,
264    "hot-list",
265    futu_core::proto_id::QOT_GET_HOT_LIST,
266    qot_get_hot_list
267);
268qot_proto_json_command!(
269    run_short_selling_rank,
270    "short-selling-rank",
271    futu_core::proto_id::QOT_GET_SHORT_SELLING_RANK,
272    qot_get_short_selling_rank
273);
274qot_proto_json_command!(
275    run_period_change_rank,
276    "period-change-rank",
277    futu_core::proto_id::QOT_GET_PERIOD_CHANGE_RANK,
278    qot_get_period_change_rank
279);
280qot_proto_json_command!(
281    run_high_dividend_soe_rank,
282    "high-dividend-soe-rank",
283    futu_core::proto_id::QOT_GET_HIGH_DIVIDEND_SOE_RANK,
284    qot_get_high_dividend_soe_rank
285);
286qot_proto_json_command!(
287    run_institution_list,
288    "institution-list",
289    futu_core::proto_id::QOT_GET_INSTITUTION_LIST,
290    qot_get_institution_list
291);
292qot_proto_json_command!(
293    run_institution_profile,
294    "institution-profile",
295    futu_core::proto_id::QOT_GET_INSTITUTION_PROFILE,
296    qot_get_institution_profile
297);
298qot_proto_json_command!(
299    run_institution_distribution,
300    "institution-distribution",
301    futu_core::proto_id::QOT_GET_INSTITUTION_DISTRIBUTION,
302    qot_get_institution_distribution
303);
304qot_proto_json_command!(
305    run_institution_holding_change,
306    "institution-holding-change",
307    futu_core::proto_id::QOT_GET_INSTITUTION_HOLDING_CHANGE,
308    qot_get_institution_holding_change
309);
310qot_proto_json_command!(
311    run_institution_holding_list,
312    "institution-holding-list",
313    futu_core::proto_id::QOT_GET_INSTITUTION_HOLDING_LIST,
314    qot_get_institution_holding_list
315);
316qot_proto_json_command!(
317    run_ark_fund_holding,
318    "ark-fund-holding",
319    futu_core::proto_id::QOT_GET_ARK_FUND_HOLDING,
320    qot_get_ark_fund_holding
321);
322qot_proto_json_command!(
323    run_ark_stock_dynamic,
324    "ark-stock-dynamic",
325    futu_core::proto_id::QOT_GET_ARK_STOCK_DYNAMIC,
326    qot_get_ark_stock_dynamic
327);
328qot_proto_json_command!(
329    run_ark_active_transaction,
330    "ark-active-transaction",
331    futu_core::proto_id::QOT_GET_ARK_ACTIVE_TRANSACTION,
332    qot_get_ark_active_transaction
333);
334qot_proto_json_command!(
335    run_rating_change,
336    "rating-change",
337    futu_core::proto_id::QOT_GET_RATING_CHANGE,
338    qot_get_rating_change
339);
340qot_proto_json_command!(
341    run_search_quote,
342    "search-quote",
343    futu_core::proto_id::QOT_GET_SEARCH_QUOTE,
344    qot_get_search_quote
345);
346qot_proto_json_command!(
347    run_search_news,
348    "search-news",
349    futu_core::proto_id::QOT_GET_SEARCH_NEWS,
350    qot_get_search_news
351);
352qot_proto_json_command!(
353    run_indicator_list,
354    "indicator-list",
355    futu_core::proto_id::QOT_GET_INDICATOR_LIST,
356    qot_get_indicator_list
357);
358qot_proto_json_command!(
359    run_option_market_statistic,
360    "option-market-statistic",
361    futu_core::proto_id::QOT_GET_OPTION_MARKET_STATISTIC,
362    qot_get_option_market_statistic
363);
364qot_proto_json_command!(
365    run_option_underlying_his_statistic,
366    "option-underlying-his-statistic",
367    futu_core::proto_id::QOT_GET_OPTION_UNDERLYING_HIS_STATISTIC,
368    qot_get_option_underlying_his_statistic
369);
370qot_proto_json_command!(
371    run_option_underlying_overview,
372    "option-underlying-overview",
373    futu_core::proto_id::QOT_GET_OPTION_UNDERLYING_OVERVIEW,
374    qot_get_option_underlying_overview
375);
376qot_proto_json_command!(
377    run_option_underlying_his_volatility,
378    "option-underlying-his-volatility",
379    futu_core::proto_id::QOT_GET_OPTION_UNDERLYING_HIS_VOLATILITY,
380    qot_get_option_underlying_his_volatility
381);
382qot_proto_json_command!(
383    run_option_underlying_rank,
384    "option-underlying-rank",
385    futu_core::proto_id::QOT_GET_OPTION_UNDERLYING_RANK,
386    qot_get_option_underlying_rank
387);
388qot_proto_json_command!(
389    run_option_rank,
390    "option-rank",
391    futu_core::proto_id::QOT_GET_OPTION_RANK,
392    qot_get_option_rank
393);
394qot_proto_json_command!(
395    run_option_event,
396    "option-event",
397    futu_core::proto_id::QOT_GET_OPTION_EVENT,
398    qot_get_option_event
399);
400qot_proto_json_command!(
401    run_option_event_alert,
402    "option-event-alert",
403    futu_core::proto_id::QOT_GET_OPTION_EVENT_ALERT,
404    qot_get_option_event_alert
405);
406qot_proto_json_command!(
407    run_set_option_event_alert,
408    "set-option-event-alert",
409    futu_core::proto_id::QOT_SET_OPTION_EVENT_ALERT,
410    qot_set_option_event_alert
411);
412qot_proto_json_command!(
413    run_option_zero_dte_screener,
414    "option-zero-dte-screener",
415    futu_core::proto_id::QOT_GET_OPTION_ZERO_DTE_SCREENER,
416    qot_get_option_zero_dte_screener
417);
418qot_proto_json_command!(
419    run_option_zero_dte_contract,
420    "option-zero-dte-contract",
421    futu_core::proto_id::QOT_GET_OPTION_ZERO_DTE_CONTRACT,
422    qot_get_option_zero_dte_contract
423);
424qot_proto_json_command!(
425    run_option_earnings_screener,
426    "option-earnings-screener",
427    futu_core::proto_id::QOT_GET_OPTION_EARNINGS_SCREENER,
428    qot_get_option_earnings_screener
429);
430qot_proto_json_command!(
431    run_option_seller_screener,
432    "option-seller-screener",
433    futu_core::proto_id::QOT_GET_OPTION_SELLER_SCREENER,
434    qot_get_option_seller_screener
435);
436qot_proto_json_command!(
437    run_industrial_chain_list,
438    "industrial-chain-list",
439    futu_core::proto_id::QOT_GET_INDUSTRIAL_CHAIN_LIST,
440    qot_get_industrial_chain_list
441);
442qot_proto_json_command!(
443    run_industrial_chain_detail,
444    "industrial-chain-detail",
445    futu_core::proto_id::QOT_GET_INDUSTRIAL_CHAIN_DETAIL,
446    qot_get_industrial_chain_detail
447);
448qot_proto_json_command!(
449    run_industrial_chain_by_plate,
450    "industrial-chain-by-plate",
451    futu_core::proto_id::QOT_GET_INDUSTRIAL_CHAIN_BY_PLATE,
452    qot_get_industrial_chain_by_plate
453);
454qot_proto_json_command!(
455    run_industrial_plate_info,
456    "industrial-plate-info",
457    futu_core::proto_id::QOT_GET_INDUSTRIAL_PLATE_INFO,
458    qot_get_industrial_plate_info
459);
460qot_proto_json_command!(
461    run_industrial_plate_stock,
462    "industrial-plate-stock",
463    futu_core::proto_id::QOT_GET_INDUSTRIAL_PLATE_STOCK,
464    qot_get_industrial_plate_stock
465);
466qot_proto_json_command!(
467    run_heat_map_data,
468    "heat-map-data",
469    futu_core::proto_id::QOT_GET_HEAT_MAP_DATA,
470    qot_get_heat_map_data
471);
472qot_proto_json_command!(
473    run_rise_fall_distribution,
474    "rise-fall-distribution",
475    futu_core::proto_id::QOT_GET_RISE_FALL_DISTRIBUTION,
476    qot_get_rise_fall_distribution
477);
478
479#[cfg(test)]
480mod tests;