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