Skip to main content

futu_mcp/tool_args/
qot.rs

1//! v1.4.110 P1-1: 拆自 `tool_args.rs` 按 handler 域分组.
2
3use rmcp::schemars;
4use serde::Deserialize;
5
6use futu_core::qot_endpoint_market;
7
8use crate::tool_enums;
9
10use super::*;
11
12mod basic;
13mod f10;
14mod misc;
15mod price_reminder;
16mod reference;
17mod screen_unusual;
18mod shareholders;
19
20pub use basic::{
21    KLineReq, MarketStateReq, OptionExerciseProbabilityReq, OptionVolatilityReq, OrderBookReq,
22    PlateListReq, PlateStocksReq, ShortInfoReq, SymbolListReq, SymbolReq, TickerReq,
23    TopTenBuySellBrokersReq,
24};
25pub use f10::{
26    CompanyExecutiveBackgroundReq, CompanyOperationalEfficiencyReq, FinancialCalendarReq,
27    FinancialsEarningsPriceHistoryReq, FinancialsEarningsPriceMoveReq,
28    FinancialsRevenueBreakdownReq, FinancialsStatementsReq, ResearchAnalystConsensusReq,
29    ResearchMorningstarReportReq, ResearchRatingSummaryReq, TargetFinancialCalendarReq,
30    ValuationDetailReq, ValuationPlateStockListReq,
31};
32pub use misc::{
33    BizGroupReq, BondSymbolReq, CodeChangeReq, HistoryKlQuotaReq, HoldingChangeReq,
34    ModifyUserSecurityReq, QuoteRightsReq, StockFilterReq, SuspendReq, TickerStatisticDetailReq,
35    TickerStatisticReq, TradingDaysReq, UserSecurityGroupReq, UserSecurityReq,
36};
37pub use price_reminder::{GetPriceReminderReq, OptionExpirationDateReq, SetPriceReminderReq};
38pub use reference::{
39    FutureInfoReq, HistoryKLineReq, IpoCalendarReq, IpoListReq, OptionChainReq, ReferenceReq,
40    WarrantReq,
41};
42pub use screen_unusual::{
43    DerivativeUnusualReq, FinancialUnusualReq, OptionScreenReq, StockScreenReq,
44    TechnicalUnusualReq, WarrantScreenReq,
45};
46pub use shareholders::{
47    InsiderHolderListReq, InsiderTradeListReq, ShareholdersHolderDetailReq,
48    ShareholdersHoldingChangesReq, ShareholdersInstitutionalReq, ShareholdersOverviewReq,
49};
50
51fn validate_optional_i32_range(
52    tool: &str,
53    field: &str,
54    value: Option<i32>,
55    min: i32,
56    max: i32,
57) -> Result<(), String> {
58    if let Some(value) = value
59        && !(min..=max).contains(&value)
60    {
61        return Err(format!(
62            "{field} must be in {min}..={max} for {tool}, got {value}"
63        ));
64    }
65    Ok(())
66}
67
68fn validate_optional_f64_min_max(
69    tool: &str,
70    field: &str,
71    min: Option<f64>,
72    max: Option<f64>,
73) -> Result<(), String> {
74    if let (Some(min), Some(max)) = (min, max)
75        && min > max
76    {
77        return Err(format!("{field}_min must be <= {field}_max for {tool}"));
78    }
79    Ok(())
80}
81
82fn deser_stock_filter_market_as_i32<'de, D>(deserializer: D) -> Result<i32, D::Error>
83where
84    D: serde::Deserializer<'de>,
85{
86    deser_endpoint_market_as_i32(
87        deserializer,
88        qot_endpoint_market::QOT_STOCK_FILTER_MARKET_VALID_VALUES,
89        qot_endpoint_market::stock_filter_market_from_str_alias,
90    )
91}
92
93fn deser_ipo_market_as_i32<'de, D>(deserializer: D) -> Result<i32, D::Error>
94where
95    D: serde::Deserializer<'de>,
96{
97    deser_endpoint_market_as_i32(
98        deserializer,
99        qot_endpoint_market::QOT_IPO_MARKET_VALID_VALUES,
100        qot_endpoint_market::ipo_market_from_str_alias,
101    )
102}
103
104fn deser_endpoint_market_as_i32<'de, D>(
105    deserializer: D,
106    valid_values: &str,
107    parse_alias: impl Fn(&str) -> Option<i32>,
108) -> Result<i32, D::Error>
109where
110    D: serde::Deserializer<'de>,
111{
112    #[derive(Deserialize)]
113    #[serde(untagged)]
114    enum IntOrStr {
115        Int(i32),
116        Str(String),
117    }
118
119    match IntOrStr::deserialize(deserializer)? {
120        IntOrStr::Int(value) => Ok(value),
121        IntOrStr::Str(value) => {
122            if let Some(value) = parse_alias(&value) {
123                return Ok(value);
124            }
125            Err(serde::de::Error::custom(format!(
126                "unknown market {value:?}: valid = {valid_values}"
127            )))
128        }
129    }
130}