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 crate::tool_enums;
7
8use super::*;
9
10mod basic;
11mod f10;
12mod misc;
13mod price_reminder;
14mod reference;
15mod screen_unusual;
16mod shareholders;
17
18pub use basic::{
19    KLineReq, MarketStateReq, OptionExerciseProbabilityReq, OptionVolatilityReq, OrderBookReq,
20    PlateListReq, PlateStocksReq, ShortInfoReq, SymbolListReq, SymbolReq, TickerReq,
21    TopTenBuySellBrokersReq,
22};
23pub use f10::{
24    CompanyExecutiveBackgroundReq, CompanyOperationalEfficiencyReq, FinancialCalendarReq,
25    FinancialsEarningsPriceHistoryReq, FinancialsEarningsPriceMoveReq,
26    FinancialsRevenueBreakdownReq, FinancialsStatementsReq, ResearchAnalystConsensusReq,
27    ResearchMorningstarReportReq, ResearchRatingSummaryReq, TargetFinancialCalendarReq,
28    ValuationDetailReq, ValuationPlateStockListReq,
29};
30pub use misc::{
31    BizGroupReq, BondSymbolReq, CodeChangeReq, HistoryKlQuotaReq, HoldingChangeReq,
32    ModifyUserSecurityReq, QuoteRightsReq, StockFilterReq, SuspendReq, TickerStatisticDetailReq,
33    TickerStatisticReq, TradingDaysReq, UserSecurityGroupReq, UserSecurityReq,
34};
35pub use price_reminder::{GetPriceReminderReq, OptionExpirationDateReq, SetPriceReminderReq};
36pub use reference::{
37    FutureInfoReq, HistoryKLineReq, IpoCalendarReq, IpoListReq, OptionChainReq, ReferenceReq,
38    WarrantReq,
39};
40pub use screen_unusual::{
41    DerivativeUnusualReq, FinancialUnusualReq, OptionScreenReq, StockScreenReq,
42    TechnicalUnusualReq, WarrantScreenReq,
43};
44pub use shareholders::{
45    InsiderHolderListReq, InsiderTradeListReq, ShareholdersHolderDetailReq,
46    ShareholdersHoldingChangesReq, ShareholdersInstitutionalReq, ShareholdersOverviewReq,
47};
48
49fn validate_optional_i32_range(
50    tool: &str,
51    field: &str,
52    value: Option<i32>,
53    min: i32,
54    max: i32,
55) -> Result<(), String> {
56    if let Some(value) = value
57        && !(min..=max).contains(&value)
58    {
59        return Err(format!(
60            "{field} must be in {min}..={max} for {tool}, got {value}"
61        ));
62    }
63    Ok(())
64}
65
66fn validate_optional_f64_min_max(
67    tool: &str,
68    field: &str,
69    min: Option<f64>,
70    max: Option<f64>,
71) -> Result<(), String> {
72    if let (Some(min), Some(max)) = (min, max)
73        && min > max
74    {
75        return Err(format!("{field}_min must be <= {field}_max for {tool}"));
76    }
77    Ok(())
78}
79
80fn deser_hk_hkfuture_us_cn_market_as_i32<'de, D>(deserializer: D) -> Result<i32, D::Error>
81where
82    D: serde::Deserializer<'de>,
83{
84    #[derive(Deserialize)]
85    #[serde(untagged)]
86    enum IntOrStr {
87        Int(i32),
88        Str(String),
89    }
90
91    match IntOrStr::deserialize(deserializer)? {
92        IntOrStr::Int(value) => Ok(value),
93        IntOrStr::Str(value) => {
94            let trimmed = value.trim();
95            if let Ok(value) = trimmed.parse::<i32>() {
96                return Ok(value);
97            }
98            match trimmed.to_ascii_uppercase().as_str() {
99                "HK" => Ok(1),
100                "HK_FUTURE" | "HKFUTURE" => Ok(2),
101                "US" => Ok(11),
102                "SH" | "CN" => Ok(21),
103                "SZ" => Ok(22),
104                "SG" => Ok(31),
105                "JP" => Ok(41),
106                "MY" => Ok(61),
107                _ => Err(serde::de::Error::custom(format!(
108                    "unknown market {value:?}: valid = HK/HK_FUTURE/US/SH/SZ/SG/JP/MY or integer 1/2/11/21/22/31/41/61"
109                ))),
110            }
111        }
112    }
113}