futu_cache/trd_cache/
order_upsert.rs1use 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 pub fn upsert_order(&self, acc_id: u64, order: CachedOrder) -> bool {
10 self.upsert_order_internal(acc_id, order, None)
11 }
12
13 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}