Skip to main content

futu_cache/
market_event.rs

1use std::collections::BTreeSet;
2
3use dashmap::DashMap;
4use futu_domain_qot_market_state::{
5    MarketEventUpdate, MarketStateSnapshot, MarketTradeDateSnapshot, TradeDateUpdatePlan,
6    merge_market_state_patch, plan_market_trade_date_update,
7};
8
9#[derive(Clone, Debug, Default, PartialEq, Eq)]
10pub struct MarketEventApplyOutcome {
11    pub market_state_updates: usize,
12    pub trade_date_updates: usize,
13    pub changed_trade_date_quote_markets: Vec<u8>,
14}
15
16#[derive(Default)]
17pub struct MarketEventCache {
18    market_states: DashMap<u32, MarketStateSnapshot>,
19    market_trade_dates: DashMap<u32, MarketTradeDateSnapshot>,
20}
21
22impl MarketEventCache {
23    #[must_use]
24    pub fn new() -> Self {
25        Self::default()
26    }
27
28    pub fn apply_batch(&self, updates: &[MarketEventUpdate]) -> MarketEventApplyOutcome {
29        let mut outcome = MarketEventApplyOutcome::default();
30        let mut changed_quote_markets = BTreeSet::new();
31
32        for update in updates {
33            match update {
34                MarketEventUpdate::MarketState(patch) => {
35                    let existing = self
36                        .market_states
37                        .get(&patch.market_id)
38                        .map(|row| row.clone());
39                    let merged = merge_market_state_patch(existing.as_ref(), patch.clone());
40                    self.market_states.insert(merged.market_id, merged);
41                    outcome.market_state_updates += 1;
42                }
43                MarketEventUpdate::MarketTradeDate(update) => {
44                    let existing = self
45                        .market_trade_dates
46                        .get(&update.market_id)
47                        .map(|row| row.clone());
48                    if let TradeDateUpdatePlan::Changed(snapshot) =
49                        plan_market_trade_date_update(existing.as_ref(), update.clone())
50                    {
51                        // C++ stores unknown market ids but only inserts a
52                        // non-UNKNOWN type into the `ReSubMkt` set. Ref:
53                        // `NNBiz_Qot_EventNotice.cpp:286-299`.
54                        if snapshot.quote_market_type != 0 {
55                            changed_quote_markets.insert(snapshot.quote_market_type);
56                        }
57                        self.market_trade_dates.insert(snapshot.market_id, snapshot);
58                        outcome.trade_date_updates += 1;
59                    }
60                }
61            }
62        }
63
64        outcome.changed_trade_date_quote_markets = changed_quote_markets.into_iter().collect();
65        outcome
66    }
67
68    #[must_use]
69    pub fn market_state(&self, market_id: u32) -> Option<MarketStateSnapshot> {
70        self.market_states.get(&market_id).map(|row| row.clone())
71    }
72
73    #[must_use]
74    pub fn market_states(&self) -> Vec<MarketStateSnapshot> {
75        let mut rows: Vec<_> = self.market_states.iter().map(|row| row.clone()).collect();
76        rows.sort_by_key(|row| row.market_id);
77        rows
78    }
79
80    #[must_use]
81    pub fn market_trade_date(&self, market_id: u32) -> Option<MarketTradeDateSnapshot> {
82        self.market_trade_dates
83            .get(&market_id)
84            .map(|row| row.clone())
85    }
86}