Skip to main content

futu_trd/
parsing.rs

1//! Shared user-facing trade enum parsers.
2//!
3//! Keep CLI / MCP / REST adapter parsing knowledge in `futu-trd` so schema and
4//! runtime surfaces do not drift when `Trd_Common.proto` grows a variant.
5
6use crate::types::{ModifyOrderOp, OrderType, TrdEnv, TrdSide};
7
8pub const TRD_ENV_PARSE_CHOICES: &str = "real|simulate|sim";
9pub const TRD_SIDE_PARSE_CHOICES: &str = "BUY|SELL|SELL_SHORT|SHORT|BUY_BACK|COVER";
10pub const ORDER_TYPE_PARSE_CHOICES: &str = "NORMAL|LIMIT|MARKET|ABSOLUTE_LIMIT|AUCTION|\
11AUCTION_LIMIT|SPECIAL_LIMIT|SPECIAL_LIMIT_ALL|STOP|STOP_LIMIT|MIT|LIT|\
12TRAILING_STOP|TRAILING_STOP_LIMIT|TWAP_MARKET|TWAP_LIMIT|VWAP_MARKET|VWAP_LIMIT \
13or compact aliases / official OrderType int";
14pub const MODIFY_OP_PARSE_CHOICES: &str = "NORMAL|MODIFY|CANCEL|DISABLE|ENABLE|DELETE";
15
16/// Official `Trd_Common.OrderType` integers accepted by write surfaces.
17pub const ORDER_TYPE_INT_VALUES: &[i32] =
18    &[1, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19];
19
20#[must_use]
21pub fn parse_trd_env(raw: &str) -> Option<TrdEnv> {
22    match raw.trim().to_ascii_lowercase().as_str() {
23        "simulate" | "sim" => Some(TrdEnv::Simulate),
24        "real" => Some(TrdEnv::Real),
25        _ => None,
26    }
27}
28
29#[must_use]
30pub fn parse_trd_side(raw: &str) -> Option<TrdSide> {
31    match raw.trim().to_ascii_uppercase().as_str() {
32        "BUY" => Some(TrdSide::Buy),
33        "SELL" => Some(TrdSide::Sell),
34        "SELL_SHORT" | "SHORT" => Some(TrdSide::SellShort),
35        "BUY_BACK" | "COVER" => Some(TrdSide::BuyBack),
36        _ => None,
37    }
38}
39
40#[must_use]
41pub fn parse_order_type(raw: &str) -> Option<OrderType> {
42    let upper = raw.trim().to_ascii_uppercase();
43    let compact = upper.replace(['_', '-'], "");
44    match upper.as_str() {
45        "NORMAL" | "LIMIT" => Some(OrderType::Normal),
46        "MARKET" => Some(OrderType::Market),
47        "ABSOLUTE_LIMIT" => Some(OrderType::AbsoluteLimit),
48        "AUCTION" => Some(OrderType::Auction),
49        "AUCTION_LIMIT" => Some(OrderType::AuctionLimit),
50        "SPECIAL_LIMIT" => Some(OrderType::SpecialLimit),
51        "SPECIAL_LIMIT_ALL" => Some(OrderType::SpecialLimitAll),
52        "STOP" => Some(OrderType::Stop),
53        "STOP_LIMIT" | "STOP-LIMIT" => Some(OrderType::StopLimit),
54        "MIT" | "MARKET_IF_TOUCHED" => Some(OrderType::MarketifTouched),
55        "LIT" | "LIMIT_IF_TOUCHED" => Some(OrderType::LimitifTouched),
56        "TRAIL" | "TRAILING_STOP" | "TRAILING-STOP" => Some(OrderType::TrailingStop),
57        "TRAIL_LIMIT" | "TRAILING_STOP_LIMIT" => Some(OrderType::TrailingStopLimit),
58        "TWAP_MARKET" => Some(OrderType::TwapMarket),
59        "TWAP_LIMIT" => Some(OrderType::TwapLimit),
60        "VWAP_MARKET" => Some(OrderType::VwapMarket),
61        "VWAP_LIMIT" => Some(OrderType::VwapLimit),
62        _ => match compact.as_str() {
63            "ABSOLUTELIMIT" => Some(OrderType::AbsoluteLimit),
64            "AUCTIONLIMIT" => Some(OrderType::AuctionLimit),
65            "SPECIALLIMIT" => Some(OrderType::SpecialLimit),
66            "SPECIALLIMITALL" => Some(OrderType::SpecialLimitAll),
67            "STOPLIMIT" => Some(OrderType::StopLimit),
68            "MARKETIFTOUCHED" => Some(OrderType::MarketifTouched),
69            "LIMITIFTOUCHED" => Some(OrderType::LimitifTouched),
70            "TRAILINGSTOP" => Some(OrderType::TrailingStop),
71            "TRAILLIMIT" | "TRAILINGSTOPLIMIT" => Some(OrderType::TrailingStopLimit),
72            "TWAPMARKET" => Some(OrderType::TwapMarket),
73            "TWAPLIMIT" => Some(OrderType::TwapLimit),
74            "VWAPMARKET" => Some(OrderType::VwapMarket),
75            "VWAPLIMIT" => Some(OrderType::VwapLimit),
76            _ => upper.parse::<i32>().ok().and_then(order_type_from_i32),
77        },
78    }
79}
80
81#[must_use]
82pub fn order_type_from_i32(value: i32) -> Option<OrderType> {
83    match value {
84        1 => Some(OrderType::Normal),
85        2 => Some(OrderType::Market),
86        5 => Some(OrderType::AbsoluteLimit),
87        6 => Some(OrderType::Auction),
88        7 => Some(OrderType::AuctionLimit),
89        8 => Some(OrderType::SpecialLimit),
90        9 => Some(OrderType::SpecialLimitAll),
91        10 => Some(OrderType::Stop),
92        11 => Some(OrderType::StopLimit),
93        12 => Some(OrderType::MarketifTouched),
94        13 => Some(OrderType::LimitifTouched),
95        14 => Some(OrderType::TrailingStop),
96        15 => Some(OrderType::TrailingStopLimit),
97        16 => Some(OrderType::TwapMarket),
98        17 => Some(OrderType::TwapLimit),
99        18 => Some(OrderType::VwapMarket),
100        19 => Some(OrderType::VwapLimit),
101        _ => None,
102    }
103}
104
105#[must_use]
106pub fn parse_modify_op(raw: &str) -> Option<ModifyOrderOp> {
107    match raw.trim().to_ascii_uppercase().as_str() {
108        "NORMAL" | "MODIFY" => Some(ModifyOrderOp::Normal),
109        "CANCEL" => Some(ModifyOrderOp::Cancel),
110        "DISABLE" => Some(ModifyOrderOp::Disable),
111        "ENABLE" => Some(ModifyOrderOp::Enable),
112        "DELETE" => Some(ModifyOrderOp::Delete),
113        _ => None,
114    }
115}
116
117#[cfg(test)]
118mod tests;