1use 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,
25 FinancialsEarningsPriceHistoryReq, FinancialsEarningsPriceMoveReq,
26 FinancialsRevenueBreakdownReq, FinancialsStatementsReq, ResearchAnalystConsensusReq,
27 ResearchMorningstarReportReq, ResearchRatingSummaryReq, ValuationDetailReq,
28 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, IpoListReq, OptionChainReq, ReferenceReq, WarrantReq,
38};
39pub use screen_unusual::{
40 DerivativeUnusualReq, FinancialUnusualReq, OptionScreenReq, StockScreenReq,
41 TechnicalUnusualReq, WarrantScreenReq,
42};
43pub use shareholders::{
44 InsiderHolderListReq, InsiderTradeListReq, ShareholdersHolderDetailReq,
45 ShareholdersHoldingChangesReq, ShareholdersInstitutionalReq, ShareholdersOverviewReq,
46};
47
48fn validate_optional_i32_range(
49 tool: &str,
50 field: &str,
51 value: Option<i32>,
52 min: i32,
53 max: i32,
54) -> Result<(), String> {
55 if let Some(value) = value
56 && !(min..=max).contains(&value)
57 {
58 return Err(format!(
59 "{field} must be in {min}..={max} for {tool}, got {value}"
60 ));
61 }
62 Ok(())
63}
64
65fn validate_optional_f64_min_max(
66 tool: &str,
67 field: &str,
68 min: Option<f64>,
69 max: Option<f64>,
70) -> Result<(), String> {
71 if let (Some(min), Some(max)) = (min, max)
72 && min > max
73 {
74 return Err(format!("{field}_min must be <= {field}_max for {tool}"));
75 }
76 Ok(())
77}
78
79fn deser_hk_hkfuture_us_cn_market_as_i32<'de, D>(deserializer: D) -> Result<i32, D::Error>
80where
81 D: serde::Deserializer<'de>,
82{
83 #[derive(Deserialize)]
84 #[serde(untagged)]
85 enum IntOrStr {
86 Int(i32),
87 Str(String),
88 }
89
90 match IntOrStr::deserialize(deserializer)? {
91 IntOrStr::Int(value) => Ok(value),
92 IntOrStr::Str(value) => {
93 let trimmed = value.trim();
94 if let Ok(value) = trimmed.parse::<i32>() {
95 return Ok(value);
96 }
97 match trimmed.to_ascii_uppercase().as_str() {
98 "HK" => Ok(1),
99 "HK_FUTURE" | "HKFUTURE" => Ok(2),
100 "US" => Ok(11),
101 "SH" | "CN" => Ok(21),
102 "SZ" => Ok(22),
103 "SG" => Ok(31),
104 "JP" => Ok(41),
105 "MY" => Ok(61),
106 _ => Err(serde::de::Error::custom(format!(
107 "unknown market {value:?}: valid = HK/HK_FUTURE/US/SH/SZ/SG/JP/MY or integer 1/2/11/21/22/31/41/61"
108 ))),
109 }
110 }
111 }
112}