Skip to main content

futu_mcp/handlers/trade/
helpers.rs

1//! mcp/handlers/trade/helpers — parse_* + build_header_* (跨 trade sub-mod 共用)
2//! (v1.4.110 CC Batch O: 拆自 trade.rs L26-110)
3
4use anyhow::{Result, bail};
5use futu_trd::types::{TrdEnv, TrdHeader, TrdMarket};
6
7pub fn parse_trd_market(s: &str) -> Result<TrdMarket> {
8    // v1.4.93 BUG-001 fix (S level ship-blocker): 9 variants 对齐
9    // `Trd_Common.proto::TrdMarket` + MCP schema. 也接 int 值 (per Trd_Common.proto).
10    // 5 国 (SG/AU/JP/MY/CA) + Futures=5 在 v1.4.86-90 只 4 variants 时挂.
11    //
12    // v1.4.102 fund-market handoff (per pitfall #54): 加 HKFUND=113 / USFUND=123
13    // view-only 融资融券 / 基金账户.
14    let trimmed = s.trim();
15    let upper = trimmed.to_ascii_uppercase();
16    let m = match upper.as_str() {
17        "HK" | "1" => TrdMarket::HK,
18        "US" | "2" => TrdMarket::US,
19        "CN" | "3" => TrdMarket::CN,
20        "HKCC" | "4" => TrdMarket::HKCC,
21        "FUTURES" | "5" => TrdMarket::Futures,
22        "SG" | "6" => TrdMarket::SG,
23        "AU" | "8" => TrdMarket::AU,
24        "JP" | "15" => TrdMarket::JP,
25        "MY" | "111" => TrdMarket::MY,
26        "CA" | "112" => TrdMarket::CA,
27        "HKFUND" | "HK_FUND" | "113" => TrdMarket::HKFund,
28        "USFUND" | "US_FUND" | "123" => TrdMarket::USFund,
29        other => bail!(
30            "unknown trd market {other:?} \
31             (HK|US|CN|HKCC|FUTURES|SG|AU|JP|MY|CA|HKFUND|USFUND \
32             or int 1/2/3/4/5/6/8/15/111/112/113/123 per Trd_Common.proto)"
33        ),
34    };
35    Ok(m)
36}
37
38pub fn parse_trd_env(s: &str) -> Result<TrdEnv> {
39    let e = match s.trim().to_ascii_lowercase().as_str() {
40        "simulate" | "sim" => TrdEnv::Simulate,
41        "real" => TrdEnv::Real,
42        other => bail!("unknown trd env {other:?} (real|simulate)"),
43    };
44    Ok(e)
45}
46
47/// v1.4.102 codex 38 F4 / 41 F2 / 41 F3 / 42 F3 (P2): 严格 env → i32
48/// 共享 helper, 替换 cash_flow / margin_info / account_flag / bond 4 处
49/// 旧"non-real => 0 silent"模式. typo (e.g. "reel" / "prod") 现在 reject
50/// at MCP boundary (BUG-005 反 silent wrong-env).
51pub fn parse_trd_env_int(s: &str) -> Result<i32> {
52    Ok(parse_trd_env(s)? as i32)
53}
54
55/// v1.4.102 codex 33 F5 / 34 F4 / 35 F4 (P2): 拒 fund market 在 calculation /
56/// active trade-read path. 用于 max_trd_qtys / margin_ratio / order_fee /
57/// orders / deals 等 active backend call (区别于 view-only positions/funds/
58/// cash-log/history 真机 verified).
59pub fn parse_trd_market_strict_no_fund(s: &str) -> Result<TrdMarket> {
60    let m = parse_trd_market(s)?;
61    match m {
62        TrdMarket::HKFund | TrdMarket::USFund => bail!(
63            "trd market HKFUND/USFUND (113/123) 仅 view-only read endpoints \
64             (positions/funds/cash-log/history-orders/history-fills) 真机 verified; \
65             active/calculation path (orders/deals/max_trd_qtys/margin_ratio/order_fee) \
66             用主市场. v1.4.102 codex 33 F5 / 34 F4 / 35 F4 fix."
67        ),
68        _ => Ok(m),
69    }
70}
71
72/// v1.4.102 codex 35 F4: build_header for active/calculation MCP tools.
73/// view-only read tools (positions/funds/cash-log/history) 用普通 build_header
74/// 接受 fund market.
75pub fn build_header_strict_no_fund(env: &str, acc_id: u64, market: &str) -> Result<TrdHeader> {
76    Ok(TrdHeader {
77        trd_env: parse_trd_env(env)?,
78        acc_id,
79        trd_market: parse_trd_market_strict_no_fund(market)?,
80        jp_acc_type: None,
81    })
82}
83
84pub fn build_header(env: &str, acc_id: u64, market: &str) -> Result<TrdHeader> {
85    Ok(TrdHeader {
86        trd_env: parse_trd_env(env)?,
87        acc_id,
88        trd_market: parse_trd_market(market)?,
89        jp_acc_type: None,
90    })
91}