Skip to main content

futu_cache/trd_cache/
order_upsert.rs

1use super::{CachedOrder, OrderRelationSourceToken, TrdCache};
2use futu_domain_trade_account::{
3    BackendOrderIdentityFacts, OrderUpsertAction, OrderUpsertOrderFacts,
4    backend_order_identity_matches_like_cpp, plan_order_upsert_like_cpp,
5};
6
7impl TrdCache {
8    /// Update or insert a single order for push-driven order state.
9    pub fn upsert_order(&self, acc_id: u64, order: CachedOrder) -> bool {
10        self.upsert_order_internal(acc_id, order, None)
11    }
12
13    /// Apply one source-fenced backend push/detail row.
14    pub fn upsert_order_from_source(
15        &self,
16        acc_id: u64,
17        order: CachedOrder,
18        source: OrderRelationSourceToken,
19    ) -> bool {
20        self.upsert_order_internal(acc_id, order, Some(source))
21    }
22
23    fn upsert_order_internal(
24        &self,
25        acc_id: u64,
26        order: CachedOrder,
27        source: Option<OrderRelationSourceToken>,
28    ) -> bool {
29        let incoming_is_incomplete = order.is_stub || order.is_pending_broker_confirm;
30        let relation_lock = self.order_relation_snapshot_lock(acc_id);
31        let _relation_guard = relation_lock.lock();
32        let state = self.order_relation_snapshot_state(acc_id);
33        if let Some(source) = source
34            && !Self::order_relation_source_is_admitted(&state, source)
35        {
36            return false;
37        }
38        let mut entry = self.orders.entry(acc_id).or_default();
39        let changed = if let Some(existing) = entry
40            .iter_mut()
41            .find(|existing| Self::order_identity_matches(existing, &order))
42        {
43            match plan_order_upsert_like_cpp(
44                Some(Self::order_upsert_facts(existing)),
45                Self::order_upsert_facts(&order),
46            ) {
47                OrderUpsertAction::ReplaceExisting => {
48                    *existing = order;
49                    true
50                }
51                OrderUpsertAction::IgnoreStaleVersion
52                | OrderUpsertAction::IgnoreAuthoritativeExisting
53                | OrderUpsertAction::IgnoreUnchanged => false,
54                OrderUpsertAction::InsertNew => unreachable!("existing order was already matched"),
55            }
56        } else {
57            match plan_order_upsert_like_cpp(None, Self::order_upsert_facts(&order)) {
58                OrderUpsertAction::InsertNew => {
59                    entry.push(order);
60                    true
61                }
62                OrderUpsertAction::ReplaceExisting
63                | OrderUpsertAction::IgnoreStaleVersion
64                | OrderUpsertAction::IgnoreAuthoritativeExisting
65                | OrderUpsertAction::IgnoreUnchanged => {
66                    unreachable!("missing order should plan an insert")
67                }
68            }
69        };
70        if changed {
71            let unknown_source = source
72                .is_some_and(|source| source.channel == super::OrderRelationSourceChannel::Unknown);
73            let availability = if unknown_source
74                || incoming_is_incomplete
75                || state.availability != super::OrderRelationSnapshotAvailability::Fresh
76            {
77                super::OrderRelationSnapshotAvailability::Stale
78            } else {
79                super::OrderRelationSnapshotAvailability::Fresh
80            };
81            self.publish_order_relation_snapshot_state(acc_id, availability, source);
82        }
83        changed
84    }
85
86    fn order_identity_matches(existing: &CachedOrder, incoming: &CachedOrder) -> bool {
87        backend_order_identity_matches_like_cpp(BackendOrderIdentityFacts {
88            existing_order_id: existing.order_id,
89            existing_backend_order_id: &existing.backend_order_id,
90            existing_order_id_ex: &existing.order_id_ex,
91            incoming_order_id: incoming.order_id,
92            incoming_backend_order_id: &incoming.backend_order_id,
93            incoming_order_id_ex: &incoming.order_id_ex,
94        })
95    }
96
97    fn order_upsert_facts(order: &CachedOrder) -> OrderUpsertOrderFacts<'_> {
98        OrderUpsertOrderFacts {
99            order_version: order.order_version,
100            order_status: order.order_status,
101            price: order.price,
102            qty: order.qty,
103            fill_qty: order.fill_qty,
104            aux_price: order.aux_price,
105            trail_type: order.trail_type,
106            trail_value: order.trail_value,
107            trail_spread: order.trail_spread,
108            is_stub: order.is_stub,
109            is_local_order: order.is_local_order,
110            is_pending_broker_confirm: order.is_pending_broker_confirm,
111            backend_order_id: &order.backend_order_id,
112            order_id_ex: &order.order_id_ex,
113            parent_order_id_ex: order.parent_order_id_ex.as_deref(),
114            order_group_id: order.order_group_id.as_deref(),
115            order_group_type_raw: order.order_group_type_raw,
116        }
117    }
118}