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|MOC \
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/// v1.8 project-local public MOC value. The command-spec build guard proves
22/// the pinned official C++ OrderType snapshot does not own 20; requests must
23/// also carry `Trd_PlaceOrder.C2S.extensionVersion=1`.
24pub const ORDER_TYPE_MOC: i32 = 20;
25
26/// User-facing aliases exposed by MCP/tool schemas for `Trd_Common.OrderType`.
27pub const ORDER_TYPE_STRING_VALUES: &[&str] = &[
28    "NORMAL",
29    "MARKET",
30    "ABSOLUTE_LIMIT",
31    "AUCTION",
32    "AUCTION_LIMIT",
33    "SPECIAL_LIMIT",
34    "SPECIAL_LIMIT_ALL",
35    "STOP",
36    "STOP_LIMIT",
37    "MIT",
38    "LIT",
39    "TRAILING_STOP",
40    "TRAILING_STOP_LIMIT",
41    "TWAP_MARKET",
42    "TWAP_LIMIT",
43    "VWAP_MARKET",
44    "VWAP_LIMIT",
45    "MOC",
46];
47
48/// Public `Trd_Common.OrderType` integers accepted by write surfaces. Value
49/// 20 is the guarded v1.8 local MOC extension; the other values are official.
50pub const ORDER_TYPE_INT_VALUES: &[i32] = &[
51    1,
52    2,
53    5,
54    6,
55    7,
56    8,
57    9,
58    10,
59    11,
60    12,
61    13,
62    14,
63    15,
64    16,
65    17,
66    18,
67    19,
68    ORDER_TYPE_MOC,
69];
70
71#[must_use]
72pub fn parse_trd_env_id(raw: &str) -> Option<i32> {
73    match raw.trim().to_ascii_lowercase().as_str() {
74        "simulate" | "sim" => Some(0),
75        "real" => Some(1),
76        _ => None,
77    }
78}
79
80#[must_use]
81pub fn parse_trd_side_id(raw: &str) -> Option<i32> {
82    match raw.trim().to_ascii_uppercase().as_str() {
83        "BUY" => Some(1),
84        "SELL" => Some(2),
85        "SELL_SHORT" | "SHORT" => Some(3),
86        "BUY_BACK" | "COVER" => Some(4),
87        _ => None,
88    }
89}
90
91#[must_use]
92pub fn trd_side_label_from_id(value: i32) -> Option<&'static str> {
93    match value {
94        1 => Some("BUY"),
95        2 => Some("SELL"),
96        3 => Some("SELL_SHORT"),
97        4 => Some("BUY_BACK"),
98        _ => None,
99    }
100}
101
102#[must_use]
103pub fn parse_order_type_id(raw: &str) -> Option<i32> {
104    let upper = raw.trim().to_ascii_uppercase();
105    let compact = upper.replace(['_', '-'], "");
106    match upper.as_str() {
107        "NORMAL" | "LIMIT" => Some(1),
108        "MARKET" => Some(2),
109        "ABSOLUTE_LIMIT" => Some(5),
110        "AUCTION" => Some(6),
111        "AUCTION_LIMIT" => Some(7),
112        "SPECIAL_LIMIT" => Some(8),
113        "SPECIAL_LIMIT_ALL" => Some(9),
114        "STOP" => Some(10),
115        "STOP_LIMIT" | "STOP-LIMIT" => Some(11),
116        "MIT" | "MARKET_IF_TOUCHED" => Some(12),
117        "LIT" | "LIMIT_IF_TOUCHED" => Some(13),
118        "TRAIL" | "TRAILING_STOP" | "TRAILING-STOP" => Some(14),
119        "TRAIL_LIMIT" | "TRAILING_STOP_LIMIT" => Some(15),
120        "TWAP_MARKET" => Some(16),
121        "TWAP_LIMIT" => Some(17),
122        "VWAP_MARKET" => Some(18),
123        "VWAP_LIMIT" => Some(19),
124        "MOC" | "MARKET_ON_CLOSE" => Some(ORDER_TYPE_MOC),
125        _ => match compact.as_str() {
126            "ABSOLUTELIMIT" => Some(5),
127            "AUCTIONLIMIT" => Some(7),
128            "SPECIALLIMIT" => Some(8),
129            "SPECIALLIMITALL" => Some(9),
130            "STOPLIMIT" => Some(11),
131            "MARKETIFTOUCHED" => Some(12),
132            "LIMITIFTOUCHED" => Some(13),
133            "TRAILINGSTOP" => Some(14),
134            "TRAILLIMIT" | "TRAILINGSTOPLIMIT" => Some(15),
135            "TWAPMARKET" => Some(16),
136            "TWAPLIMIT" => Some(17),
137            "VWAPMARKET" => Some(18),
138            "VWAPLIMIT" => Some(19),
139            "MOC" | "MARKETONCLOSE" => Some(ORDER_TYPE_MOC),
140            _ => upper.parse::<i32>().ok().and_then(order_type_id_from_i32),
141        },
142    }
143}
144
145#[must_use]
146pub fn order_type_id_from_i32(value: i32) -> Option<i32> {
147    ORDER_TYPE_INT_VALUES.contains(&value).then_some(value)
148}
149
150#[must_use]
151pub fn parse_modify_op_id(raw: &str) -> Option<i32> {
152    modify_op_id_from_str(raw)
153}
154
155#[must_use]
156pub fn modify_op_id_from_str(raw: &str) -> Option<i32> {
157    match raw.trim().to_ascii_uppercase().as_str() {
158        "NORMAL" | "MODIFY" => Some(MODIFY_ORDER_OP_NORMAL),
159        "CANCEL" => Some(MODIFY_ORDER_OP_CANCEL),
160        "DISABLE" => Some(MODIFY_ORDER_OP_DISABLE),
161        "ENABLE" => Some(MODIFY_ORDER_OP_ENABLE),
162        "DELETE" => Some(MODIFY_ORDER_OP_DELETE),
163        _ => None,
164    }
165}