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