Skip to main content

futu_core/
trade_parsing.rs

1//! User-facing raw parsers for `Trd_Common` enums shared by trade surfaces.
2//!
3//! Keep raw protocol enum parsing in core so CLI/MCP/REST and typed trade
4//! facades do not each maintain their own string/int alias tables.
5
6pub const TRD_ENV_PARSE_CHOICES: &str = "real|simulate|sim";
7pub const TRD_SIDE_PARSE_CHOICES: &str = "BUY|SELL|SELL_SHORT|SHORT|BUY_BACK|COVER";
8pub const ORDER_TYPE_PARSE_CHOICES: &str = "NORMAL|LIMIT|MARKET|ABSOLUTE_LIMIT|AUCTION|\
9AUCTION_LIMIT|SPECIAL_LIMIT|SPECIAL_LIMIT_ALL|STOP|STOP_LIMIT|MIT|LIT|\
10TRAILING_STOP|TRAILING_STOP_LIMIT|TWAP_MARKET|TWAP_LIMIT|VWAP_MARKET|VWAP_LIMIT \
11or compact aliases / official OrderType int";
12pub const MODIFY_OP_PARSE_CHOICES: &str = "NORMAL|MODIFY|CANCEL|DISABLE|ENABLE|DELETE";
13
14// Ref: `Trd_Common.proto` enum `ModifyOrderOp`.
15pub const MODIFY_ORDER_OP_NORMAL: i32 = 1;
16pub const MODIFY_ORDER_OP_CANCEL: i32 = 2;
17pub const MODIFY_ORDER_OP_DISABLE: i32 = 3;
18pub const MODIFY_ORDER_OP_ENABLE: i32 = 4;
19pub const MODIFY_ORDER_OP_DELETE: i32 = 5;
20
21/// User-facing aliases exposed by MCP/tool schemas for `Trd_Common.OrderType`.
22pub const ORDER_TYPE_STRING_VALUES: &[&str] = &[
23    "NORMAL",
24    "MARKET",
25    "ABSOLUTE_LIMIT",
26    "AUCTION",
27    "AUCTION_LIMIT",
28    "SPECIAL_LIMIT",
29    "SPECIAL_LIMIT_ALL",
30    "STOP",
31    "STOP_LIMIT",
32    "MIT",
33    "LIT",
34    "TRAILING_STOP",
35    "TRAILING_STOP_LIMIT",
36    "TWAP_MARKET",
37    "TWAP_LIMIT",
38    "VWAP_MARKET",
39    "VWAP_LIMIT",
40];
41
42/// Official `Trd_Common.OrderType` integers accepted by write surfaces.
43pub const ORDER_TYPE_INT_VALUES: &[i32] =
44    &[1, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19];
45
46#[must_use]
47pub fn parse_trd_env_id(raw: &str) -> Option<i32> {
48    match raw.trim().to_ascii_lowercase().as_str() {
49        "simulate" | "sim" => Some(0),
50        "real" => Some(1),
51        _ => None,
52    }
53}
54
55#[must_use]
56pub fn parse_trd_side_id(raw: &str) -> Option<i32> {
57    match raw.trim().to_ascii_uppercase().as_str() {
58        "BUY" => Some(1),
59        "SELL" => Some(2),
60        "SELL_SHORT" | "SHORT" => Some(3),
61        "BUY_BACK" | "COVER" => Some(4),
62        _ => None,
63    }
64}
65
66#[must_use]
67pub fn trd_side_label_from_id(value: i32) -> Option<&'static str> {
68    match value {
69        1 => Some("BUY"),
70        2 => Some("SELL"),
71        3 => Some("SELL_SHORT"),
72        4 => Some("BUY_BACK"),
73        _ => None,
74    }
75}
76
77#[must_use]
78pub fn parse_order_type_id(raw: &str) -> Option<i32> {
79    let upper = raw.trim().to_ascii_uppercase();
80    let compact = upper.replace(['_', '-'], "");
81    match upper.as_str() {
82        "NORMAL" | "LIMIT" => Some(1),
83        "MARKET" => Some(2),
84        "ABSOLUTE_LIMIT" => Some(5),
85        "AUCTION" => Some(6),
86        "AUCTION_LIMIT" => Some(7),
87        "SPECIAL_LIMIT" => Some(8),
88        "SPECIAL_LIMIT_ALL" => Some(9),
89        "STOP" => Some(10),
90        "STOP_LIMIT" | "STOP-LIMIT" => Some(11),
91        "MIT" | "MARKET_IF_TOUCHED" => Some(12),
92        "LIT" | "LIMIT_IF_TOUCHED" => Some(13),
93        "TRAIL" | "TRAILING_STOP" | "TRAILING-STOP" => Some(14),
94        "TRAIL_LIMIT" | "TRAILING_STOP_LIMIT" => Some(15),
95        "TWAP_MARKET" => Some(16),
96        "TWAP_LIMIT" => Some(17),
97        "VWAP_MARKET" => Some(18),
98        "VWAP_LIMIT" => Some(19),
99        _ => match compact.as_str() {
100            "ABSOLUTELIMIT" => Some(5),
101            "AUCTIONLIMIT" => Some(7),
102            "SPECIALLIMIT" => Some(8),
103            "SPECIALLIMITALL" => Some(9),
104            "STOPLIMIT" => Some(11),
105            "MARKETIFTOUCHED" => Some(12),
106            "LIMITIFTOUCHED" => Some(13),
107            "TRAILINGSTOP" => Some(14),
108            "TRAILLIMIT" | "TRAILINGSTOPLIMIT" => Some(15),
109            "TWAPMARKET" => Some(16),
110            "TWAPLIMIT" => Some(17),
111            "VWAPMARKET" => Some(18),
112            "VWAPLIMIT" => Some(19),
113            _ => upper.parse::<i32>().ok().and_then(order_type_id_from_i32),
114        },
115    }
116}
117
118#[must_use]
119pub fn order_type_id_from_i32(value: i32) -> Option<i32> {
120    ORDER_TYPE_INT_VALUES.contains(&value).then_some(value)
121}
122
123#[must_use]
124pub fn parse_modify_op_id(raw: &str) -> Option<i32> {
125    modify_op_id_from_str(raw)
126}
127
128#[must_use]
129pub fn modify_op_id_from_str(raw: &str) -> Option<i32> {
130    match raw.trim().to_ascii_uppercase().as_str() {
131        "NORMAL" | "MODIFY" => Some(MODIFY_ORDER_OP_NORMAL),
132        "CANCEL" => Some(MODIFY_ORDER_OP_CANCEL),
133        "DISABLE" => Some(MODIFY_ORDER_OP_DISABLE),
134        "ENABLE" => Some(MODIFY_ORDER_OP_ENABLE),
135        "DELETE" => Some(MODIFY_ORDER_OP_DELETE),
136        _ => None,
137    }
138}