Skip to main content

futu_cache/trd_cache/
trade_write_lookup.rs

1use super::{CachedOrderSnapshot, ResolveOrderError, TrdCache};
2use futu_domain_trade_account::{
3    TradeWriteOrderIdCandidateFacts, TradeWriteOrderIdExCandidateFacts,
4    TradeWriteOrderIdExCandidateMatch, TradeWriteOrderIdSnapshotDecision,
5    TradeWriteOrderLookupRequest, TradeWriteOrderLookupRoute, TradeWriteSnapshotBackendIdPatch,
6    classify_trade_write_order_id_ex_candidate, plan_trade_write_order_id_snapshot,
7    plan_trade_write_order_lookup_route, plan_trade_write_snapshot_backend_id_patch,
8    trade_write_order_id_candidate_matches,
9};
10
11impl TrdCache {
12    /// Resolve cached order context for trade-write (modify / cancel) handlers.
13    ///
14    /// Aligns with C++ `APIServer_Trd_ModifyOrder.cpp`: prefer `orderIDEx`
15    /// (backend `szOrderID`), otherwise resolve the FTAPI order hash from the
16    /// current order cache. Cache miss is fail-closed so callers return a clear
17    /// "refresh orders or pass orderIDEx" error instead of treating the hash as
18    /// a backend order id.
19    pub fn find_order_for_trade_write(
20        &self,
21        acc_id: u64,
22        order_id: u64,
23        order_id_ex: Option<&str>,
24    ) -> Result<CachedOrderSnapshot, ResolveOrderError> {
25        match plan_trade_write_order_lookup_route(TradeWriteOrderLookupRequest {
26            order_id,
27            order_id_ex,
28        }) {
29            TradeWriteOrderLookupRoute::ByOrderIdEx { order_id_ex } => {
30                self.find_order_for_trade_write_by_order_id_ex(acc_id, order_id_ex)
31            }
32            TradeWriteOrderLookupRoute::ByOrderId { order_id } => {
33                self.find_order_for_trade_write_by_hash(acc_id, order_id)
34            }
35            TradeWriteOrderLookupRoute::RejectInvalidInput => Err(ResolveOrderError::InvalidInput),
36        }
37    }
38
39    fn find_order_for_trade_write_by_order_id_ex(
40        &self,
41        acc_id: u64,
42        order_id_ex: &str,
43    ) -> Result<CachedOrderSnapshot, ResolveOrderError> {
44        let orders = self
45            .orders
46            .get(&acc_id)
47            .ok_or(ResolveOrderError::CacheMiss)?;
48        if let Some(order) = orders.iter().find(|order| {
49            matches!(
50                classify_trade_write_order_id_ex_candidate(TradeWriteOrderIdExCandidateFacts {
51                    backend_order_id: &order.backend_order_id,
52                    order_id_ex: &order.order_id_ex,
53                    requested_order_id_ex: order_id_ex,
54                }),
55                TradeWriteOrderIdExCandidateMatch::BackendOrderId
56            )
57        }) {
58            return Ok(CachedOrderSnapshot::from_order(order));
59        }
60
61        if let Some(order) = orders.iter().find(|order| {
62            matches!(
63                classify_trade_write_order_id_ex_candidate(TradeWriteOrderIdExCandidateFacts {
64                    backend_order_id: &order.backend_order_id,
65                    order_id_ex: &order.order_id_ex,
66                    requested_order_id_ex: order_id_ex,
67                }),
68                TradeWriteOrderIdExCandidateMatch::OrderIdExField
69            )
70        }) {
71            let mut snap = CachedOrderSnapshot::from_order(order);
72            if matches!(
73                plan_trade_write_snapshot_backend_id_patch(&snap.backend_order_id),
74                TradeWriteSnapshotBackendIdPatch::UseRequestedOrderIdEx
75            ) {
76                snap.backend_order_id = order_id_ex.to_string();
77            }
78            return Ok(snap);
79        }
80
81        Err(ResolveOrderError::CacheMiss)
82    }
83
84    fn find_order_for_trade_write_by_hash(
85        &self,
86        acc_id: u64,
87        order_id: u64,
88    ) -> Result<CachedOrderSnapshot, ResolveOrderError> {
89        let orders = self
90            .orders
91            .get(&acc_id)
92            .ok_or(ResolveOrderError::CacheMiss)?;
93        let order = orders
94            .iter()
95            .find(|order| {
96                trade_write_order_id_candidate_matches(TradeWriteOrderIdCandidateFacts {
97                    cached_order_id: order.order_id,
98                    requested_order_id: order_id,
99                })
100            })
101            .ok_or(ResolveOrderError::CacheMiss)?;
102        match plan_trade_write_order_id_snapshot(&order.backend_order_id) {
103            TradeWriteOrderIdSnapshotDecision::Accept => Ok(CachedOrderSnapshot::from_order(order)),
104            TradeWriteOrderIdSnapshotDecision::RejectMissingBackendId => {
105                Err(ResolveOrderError::MissingBackendId)
106            }
107        }
108    }
109}