futu_mcp/handlers/trade/
helpers.rs1use anyhow::{Result, bail};
5use futu_core::{trade_market, trade_parsing};
6use futu_trd::types::{TrdEnv, TrdHeader, TrdMarket};
7
8use crate::trd_sdk_adapter;
9
10pub fn parse_trd_market(s: &str) -> Result<TrdMarket> {
11 let market = trade_market::parse_trd_market_id(s).ok_or_else(|| {
12 anyhow::anyhow!(
13 "unknown trd market {:?} ({})",
14 s.trim().to_ascii_uppercase(),
15 trade_market::TRD_MARKET_PARSE_CHOICES
16 )
17 })?;
18 trd_sdk_adapter::trd_market_from_id(market).ok_or_else(|| {
19 anyhow::anyhow!("unknown trd market id {market} after core parser accepted it")
20 })
21}
22
23pub fn parse_trd_env(s: &str) -> Result<TrdEnv> {
24 let env = trade_parsing::parse_trd_env_id(s).ok_or_else(|| {
25 anyhow::anyhow!(
26 "unknown trd env {:?} ({})",
27 s.trim().to_ascii_lowercase(),
28 trade_parsing::TRD_ENV_PARSE_CHOICES
29 )
30 })?;
31 trd_sdk_adapter::trd_env_from_id(env)
32 .ok_or_else(|| anyhow::anyhow!("unknown trd env id {env} after core parser accepted it"))
33}
34
35pub fn parse_trd_env_int(s: &str) -> Result<i32> {
40 Ok(parse_trd_env(s)? as i32)
41}
42
43pub fn parse_trd_market_strict_no_fund(s: &str) -> Result<TrdMarket> {
48 let m = parse_trd_market(s)?;
49 if let Some(label) = trade_market::canonical_fund_trd_market_label(m as i32) {
50 bail!(
51 "trd market {label} 仅 view-only read endpoints \
52 (positions/funds/cash-log/history-orders/history-fills) 真机 verified; \
53 active/calculation path (orders/deals/max_trd_qtys/margin_ratio/order_fee) \
54 用主市场. v1.4.102 audit 33 F5 / 34 F4 / 35 F4 fix."
55 );
56 }
57 if !trade_market::TRD_MARKET_NON_FUND_INT_VALUES.contains(&(m as i32)) {
62 bail!(
63 "trd market {} ({}) 不在 active/calculation 市场 allowlist 内 ({}); \
64 KRX=18 仅通过 batch-close-positions 原子合同放行, \
65 orders/deals/max_trd_qtys/margin_ratio/order_fee fail closed",
66 m as i32,
67 s.trim().to_ascii_uppercase(),
68 trade_market::TRD_MARKET_NON_FUND_PARSE_CHOICES
69 );
70 }
71 Ok(m)
72}
73
74pub fn build_header_strict_no_fund(env: &str, acc_id: u64, market: &str) -> Result<TrdHeader> {
78 build_header_strict_no_fund_with_jp_acc_type(env, acc_id, market, None)
79}
80
81pub fn build_header_strict_no_fund_with_jp_acc_type(
82 env: &str,
83 acc_id: u64,
84 market: &str,
85 jp_acc_type: Option<i32>,
86) -> Result<TrdHeader> {
87 Ok(TrdHeader {
88 trd_env: parse_trd_env(env)?,
89 acc_id,
90 trd_market: parse_trd_market_strict_no_fund(market)?,
91 jp_acc_type,
92 })
93}
94
95pub fn build_header(env: &str, acc_id: u64, market: &str) -> Result<TrdHeader> {
96 Ok(TrdHeader {
97 trd_env: parse_trd_env(env)?,
98 acc_id,
99 trd_market: parse_trd_market(market)?,
100 jp_acc_type: None,
101 })
102}