Skip to main content

futu_trd/currency/
labels.rs

1use super::{currency_id, security_firm_id};
2
3/// security_firm enum int → 对齐 broker 标签 (用于 error message)
4pub fn broker_label(security_firm: Option<i32>) -> &'static str {
5    match security_firm {
6        Some(security_firm_id::FUTU_HK) => "Futu HK",
7        Some(security_firm_id::FUTU_US) => "Moomoo US",
8        Some(security_firm_id::FUTU_SG) => "Moomoo SG",
9        Some(security_firm_id::FUTU_AU) => "Moomoo AU",
10        Some(security_firm_id::FUTU_CA) => "Moomoo CA",
11        Some(security_firm_id::FUTU_MY) => "Moomoo MY",
12        Some(security_firm_id::FUTU_JP) => "Moomoo JP",
13        _ => "unknown broker",
14    }
15}
16
17/// Broker 默认资金视图币种。
18///
19/// 这是 surface UX 层用于“用户未显式传 currency”时补齐 `Trd_GetFunds`
20/// 请求币种的规则,不是 C++ `CheckReqParams_GetFunds` 的参数校验规则。
21/// C++ 对 Futures / Universal 缺 `currency` 会直接 missing-parameter;Rust
22/// CLI/REST/MCP 为了让用户可直接用 App 可见 card-num 查资金,在 gateway
23/// 统一派生一个用户可预期的默认币种后再发 backend。
24///
25/// Hardcoded / Assumption Ledger:
26/// - broker enum 来自 `Trd_Common.proto::SecurityFirm` 与本文件
27///   `security_firm_id` 常量,不按具体账号硬编码。
28/// - 默认币种按券商本地记账币种选择:FutuHK=HKD, FutuInc=USD,
29///   FutuSG=SGD, FutuAU=AUD, FutuCA=CAD, FutuMY=MYR, FutuJP=JPY。
30/// - 若后续 backend 下发显式 base currency,应优先替换这张静态 broker 表。
31pub fn default_currency_by_security_firm(security_firm: Option<i32>) -> Option<i32> {
32    match security_firm? {
33        security_firm_id::FUTU_HK => Some(currency_id::HKD),
34        security_firm_id::FUTU_US => Some(currency_id::USD),
35        security_firm_id::FUTU_SG => Some(currency_id::SGD),
36        security_firm_id::FUTU_AU => Some(currency_id::AUD),
37        security_firm_id::FUTU_CA => Some(currency_id::CAD),
38        security_firm_id::FUTU_MY => Some(currency_id::MYR),
39        security_firm_id::FUTU_JP => Some(currency_id::JPY),
40        _ => None,
41    }
42}
43
44/// currency enum int → 对齐 currency 标签 (用于 error message)
45pub fn currency_label(c: i32) -> &'static str {
46    match c {
47        currency_id::HKD => "HKD",
48        currency_id::USD => "USD",
49        currency_id::CNH => "CNH",
50        currency_id::JPY => "JPY",
51        currency_id::SGD => "SGD",
52        currency_id::AUD => "AUD",
53        currency_id::CAD => "CAD",
54        currency_id::MYR => "MYR",
55        9 => "USDT", // daemon-only extension, not in C++
56        _ => "UNKNOWN",
57    }
58}
59
60/// Parse a user-facing currency code into `Trd_Common.Currency` enum int.
61///
62/// This is the shared contract for CLI / MCP / REST-like adapters that accept
63/// textual currency input. Unknown values must be rejected loudly by the caller
64/// instead of silently falling back to "no currency".
65pub fn parse_currency_label(s: &str) -> anyhow::Result<i32> {
66    match s.trim().to_ascii_uppercase().as_str() {
67        "HKD" => Ok(currency_id::HKD),
68        "USD" => Ok(currency_id::USD),
69        "CNH" | "CNY" | "RMB" => Ok(currency_id::CNH),
70        "JPY" => Ok(currency_id::JPY),
71        "SGD" => Ok(currency_id::SGD),
72        "AUD" => Ok(currency_id::AUD),
73        "CAD" => Ok(currency_id::CAD),
74        "MYR" => Ok(currency_id::MYR),
75        "USDT" => Ok(9),
76        _ => {
77            anyhow::bail!("invalid currency {s:?}: expected HKD|USD|CNH|JPY|SGD|AUD|CAD|MYR|USDT")
78        }
79    }
80}
81
82/// Display label for known currency IDs.
83///
84/// `currency_label` deliberately returns `UNKNOWN` for diagnostics. Table/JSON
85/// presentation often needs a tri-state instead: absent / unknown should stay
86/// absent so the surface can render `-`, omit a field, or choose its own
87/// fallback text.
88#[must_use]
89pub fn known_currency_label(c: Option<i32>) -> Option<&'static str> {
90    match c? {
91        currency_id::HKD => Some("HKD"),
92        currency_id::USD => Some("USD"),
93        currency_id::CNH => Some("CNH"),
94        currency_id::JPY => Some("JPY"),
95        currency_id::SGD => Some("SGD"),
96        currency_id::AUD => Some("AUD"),
97        currency_id::CAD => Some("CAD"),
98        currency_id::MYR => Some("MYR"),
99        9 => Some("USDT"),
100        _ => None,
101    }
102}
103
104/// 把 Unsupported 变 user-friendly error message (PII-free, 不暴露内部
105/// 实现细节 / 私仓源码路径 / 发版引用)
106///
107/// codex round 1 F3 (P2) v1.4.105: 之前 message 含 `APIServer_Trd_GetFunds.cpp
108/// ::CheckCurrencyValid` C++ 私仓引用 + `see CHANGELOG v1.4.105` release-process
109/// hint, 这俩不应进 user-facing public surface (`/api/funds` / MCP / CLI). 改成
110/// 只保留 broker / requested currency / supported list — 用户能看懂 + ops 能
111/// debug 即可. C++ 对齐证据保留在本 helper 上方代码注释.
112pub fn unsupported_error_message(
113    requested: i32,
114    broker_label: &str,
115    supported_labels: &[&'static str],
116) -> String {
117    format!(
118        "InvalidCurrency: account at broker {broker} does not support currency {req}. \
119         supported currencies for this account: {sup}.",
120        broker = broker_label,
121        req = currency_label(requested),
122        sup = supported_labels.join(", "),
123    )
124}
125
126/// **v1.4.106 Finding F1**: missing-currency 错误消息 (Futures / Universal 必传).
127/// 对齐 C++ `CheckReqParams_GetFunds:475-485` "missing necessary parameter
128/// Currency" 语义.
129pub fn missing_currency_error_message(
130    broker_label: &str,
131    supported_labels: &[&'static str],
132) -> String {
133    format!(
134        "MissingCurrency: futures/universal account at broker {broker} requires \
135         the `currency` parameter. supported currencies for this account: {sup}.",
136        broker = broker_label,
137        sup = supported_labels.join(", "),
138    )
139}