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