Skip to main content

futu_mcp/tool_enums/
trd_market_enum.rs

1//! Split from tool_enums.rs: TrdMarketEnum.
2
3use serde::Serialize;
4
5use futu_proto::trd_common::TrdMarket as ProtoTrdMarket;
6
7use super::ToolEnum;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, schemars::JsonSchema)]
10#[serde(into = "i32")]
11#[non_exhaustive]
12#[allow(clippy::upper_case_acronyms)] // HKCC = "HK Connect to CN", proto wire 必用此名
13pub enum TrdMarketEnum {
14    HK,
15    US,
16    CN,
17    HKCC,
18    Futures,
19    SG,
20    Crypto,
21    AU,
22    FuturesSimulateHK,
23    FuturesSimulateUS,
24    FuturesSimulateSG,
25    FuturesSimulateJP,
26    JP,
27    MY,
28    CA,
29    /// Fund markets are view-only on active write paths. Read/query tools expose
30    /// the official proto values; trade write parsers reject them explicitly.
31    HKFund,
32    USFund,
33    SGFund,
34    MYFund,
35    JPFund,
36}
37
38impl From<TrdMarketEnum> for i32 {
39    fn from(t: TrdMarketEnum) -> Self {
40        t.as_i32()
41    }
42}
43
44impl TrdMarketEnum {
45    /// v1.4.93 C3 (Option D): map a prost-generated `ProtoTrdMarket` to our
46    /// exposed subset.
47    ///
48    /// proto variants we **don't** expose (return `None`):
49    /// - `Unknown` (=0) — invalid placeholder
50    fn from_proto_variant(p: ProtoTrdMarket) -> Option<Self> {
51        Some(match p {
52            ProtoTrdMarket::Hk => Self::HK,
53            ProtoTrdMarket::Us => Self::US,
54            ProtoTrdMarket::Cn => Self::CN,
55            ProtoTrdMarket::Hkcc => Self::HKCC,
56            ProtoTrdMarket::Futures => Self::Futures,
57            ProtoTrdMarket::Sg => Self::SG,
58            ProtoTrdMarket::Crypto => Self::Crypto,
59            ProtoTrdMarket::Au => Self::AU,
60            ProtoTrdMarket::FuturesSimulateHk => Self::FuturesSimulateHK,
61            ProtoTrdMarket::FuturesSimulateUs => Self::FuturesSimulateUS,
62            ProtoTrdMarket::FuturesSimulateSg => Self::FuturesSimulateSG,
63            ProtoTrdMarket::FuturesSimulateJp => Self::FuturesSimulateJP,
64            ProtoTrdMarket::Jp => Self::JP,
65            ProtoTrdMarket::My => Self::MY,
66            ProtoTrdMarket::Ca => Self::CA,
67            ProtoTrdMarket::HkFund => Self::HKFund,
68            ProtoTrdMarket::UsFund => Self::USFund,
69            ProtoTrdMarket::SgFund => Self::SGFund,
70            ProtoTrdMarket::MyFund => Self::MYFund,
71            ProtoTrdMarket::JpFund => Self::JPFund,
72            _ => return None,
73        })
74    }
75}
76
77impl ToolEnum for TrdMarketEnum {
78    fn type_name() -> &'static str {
79        "trd_market"
80    }
81
82    fn from_i32(v: i32) -> Option<Self> {
83        Some(match v {
84            1 => Self::HK,
85            2 => Self::US,
86            3 => Self::CN,
87            4 => Self::HKCC,
88            5 => Self::Futures,
89            6 => Self::SG,
90            7 => Self::Crypto,
91            8 => Self::AU,
92            10 => Self::FuturesSimulateHK,
93            11 => Self::FuturesSimulateUS,
94            12 => Self::FuturesSimulateSG,
95            13 => Self::FuturesSimulateJP,
96            15 => Self::JP,
97            111 => Self::MY,
98            112 => Self::CA,
99            113 => Self::HKFund,
100            123 => Self::USFund,
101            124 => Self::SGFund,
102            125 => Self::MYFund,
103            126 => Self::JPFund,
104            _ => return None,
105        })
106    }
107
108    /// v1.4.93 C3 (Option D): delegate canonical-name lookup to prost-generated
109    /// `ProtoTrdMarket::from_str_name`, then map exposed variants to our local
110    /// enum. Hand-written short names (`"HK"` / `"FUTURES"` / ...) keep working
111    /// as a friendly-alias fallback so LLM agents can use either form.
112    ///
113    /// This consolidates the two enum-name lists (proto canonical + tool short)
114    /// down to one source of truth (proto). Adding a new proto variant only
115    /// requires extending [`Self::from_proto_variant`] one arm. Unrecognised
116    /// proto variants are intentionally rejected until the corresponding runtime
117    /// route has evidence; v1.4.111 exposes the 10.6 official set and lets write
118    /// parsers reject view-only fund markets at the operation boundary.
119    fn from_str(s: &str) -> Option<Self> {
120        let trimmed = s.trim();
121        let upper = trimmed.to_ascii_uppercase();
122
123        // Step 1: prost canonical names (case-sensitive: `"TrdMarket_HK"`,
124        // `"TrdMarket_Futures"`, ...). Use the trimmed-but-not-uppercased input
125        // because prost's match table is case-sensitive on its exact names.
126        // Let-chain (Rust 2024) collapses two `if let` — both must succeed.
127        // If prost matches but the variant is unexposed (e.g.
128        // `Futures_Simulate_HK` / `SG_Fund`), we fall through to the short-name
129        // attempt; in practice short-name match below will also miss, so we'll
130        // return None like before.
131        if let Some(proto) = ProtoTrdMarket::from_str_name(trimmed)
132            && let Some(local) = Self::from_proto_variant(proto)
133        {
134            return Some(local);
135        }
136
137        // Step 2: short-name aliases (uppercased for case-insensitive UX).
138        Some(match upper.as_str() {
139            "HK" => Self::HK,
140            "US" => Self::US,
141            "CN" => Self::CN,
142            "HKCC" => Self::HKCC,
143            "FUTURES" => Self::Futures,
144            "SG" => Self::SG,
145            "CRYPTO" => Self::Crypto,
146            "AU" => Self::AU,
147            "FUTURES_SIMULATE_HK" | "FUTURESSIMULATEHK" => Self::FuturesSimulateHK,
148            "FUTURES_SIMULATE_US" | "FUTURESSIMULATEUS" => Self::FuturesSimulateUS,
149            "FUTURES_SIMULATE_SG" | "FUTURESSIMULATESG" => Self::FuturesSimulateSG,
150            "FUTURES_SIMULATE_JP" | "FUTURESSIMULATEJP" => Self::FuturesSimulateJP,
151            "JP" => Self::JP,
152            "MY" => Self::MY,
153            "CA" => Self::CA,
154            "HKFUND" | "HK_FUND" => Self::HKFund,
155            "USFUND" | "US_FUND" => Self::USFund,
156            "SGFUND" | "SG_FUND" => Self::SGFund,
157            "MYFUND" | "MY_FUND" => Self::MYFund,
158            "JPFUND" | "JP_FUND" => Self::JPFund,
159            _ => return None,
160        })
161    }
162
163    fn as_i32(self) -> i32 {
164        match self {
165            Self::HK => 1,
166            Self::US => 2,
167            Self::CN => 3,
168            Self::HKCC => 4,
169            Self::Futures => 5,
170            Self::SG => 6,
171            Self::Crypto => 7,
172            Self::AU => 8,
173            Self::FuturesSimulateHK => 10,
174            Self::FuturesSimulateUS => 11,
175            Self::FuturesSimulateSG => 12,
176            Self::FuturesSimulateJP => 13,
177            Self::JP => 15,
178            Self::MY => 111,
179            Self::CA => 112,
180            Self::HKFund => 113,
181            Self::USFund => 123,
182            Self::SGFund => 124,
183            Self::MYFund => 125,
184            Self::JPFund => 126,
185        }
186    }
187
188    fn all_int_values() -> Vec<i32> {
189        vec![
190            1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 15, 111, 112, 113, 123, 124, 125, 126,
191        ]
192    }
193
194    fn all_string_values() -> Vec<&'static str> {
195        vec![
196            "HK",
197            "US",
198            "CN",
199            "HKCC",
200            "FUTURES",
201            "SG",
202            "CRYPTO",
203            "AU",
204            "FUTURES_SIMULATE_HK",
205            "FUTURES_SIMULATE_US",
206            "FUTURES_SIMULATE_SG",
207            "FUTURES_SIMULATE_JP",
208            "JP",
209            "MY",
210            "CA",
211            "HKFUND",
212            "USFUND",
213            "SGFUND",
214            "MYFUND",
215            "JPFUND",
216        ]
217    }
218}