futu_cache/
trading_session.rs1use std::collections::VecDeque;
2
3use dashmap::DashMap;
4use futu_domain_qot_calendar::{RangeSelection, TradingDay};
5use parking_lot::Mutex;
6
7use crate::market_event::MarketTradeDateToken;
8
9const SECURITY_CACHE_CAPACITY: usize = 1_024;
13const MARKET_CACHE_CAPACITY: usize = 1_024;
14
15#[derive(Debug, Clone, PartialEq, Eq, Hash)]
16pub struct SecurityTradingSessionCacheKey {
17 pub market_id: u32,
18 pub code: String,
19 pub exchange: String,
20 pub begin_date: Option<i32>,
21 pub end_date: Option<i32>,
22 pub num: Option<i32>,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq, Hash)]
26pub struct MarketTradingSessionCacheKey {
27 pub market_id: i32,
28 pub begin_date: Option<i32>,
29 pub end_date: Option<i32>,
30 pub num: Option<i32>,
31}
32
33impl MarketTradingSessionCacheKey {
34 #[must_use]
35 pub fn new(market_id: i32, range: &RangeSelection) -> Self {
36 Self {
37 market_id,
38 begin_date: range.begin_date,
39 end_date: range.end_date,
40 num: range.num,
41 }
42 }
43}
44
45#[derive(Clone)]
46struct CacheEntry {
47 token: MarketTradeDateToken,
48 days: Vec<TradingDay>,
49}
50
51#[derive(Default)]
52pub struct TradingSessionCache {
53 security: DashMap<SecurityTradingSessionCacheKey, CacheEntry>,
54 market: DashMap<MarketTradingSessionCacheKey, CacheEntry>,
55 security_order: Mutex<VecDeque<SecurityTradingSessionCacheKey>>,
56 market_order: Mutex<VecDeque<MarketTradingSessionCacheKey>>,
57}
58
59impl TradingSessionCache {
60 #[must_use]
61 pub fn security(
62 &self,
63 key: &SecurityTradingSessionCacheKey,
64 current_token: MarketTradeDateToken,
65 ) -> Option<Vec<TradingDay>> {
66 let entry = self.security.get(key)?;
67 if key.market_id != current_token.market_id || entry.token != current_token {
68 drop(entry);
69 self.security.remove(key);
70 return None;
71 }
72 Some(entry.days.clone())
73 }
74
75 pub fn insert_security_if_current(
76 &self,
77 key: SecurityTradingSessionCacheKey,
78 token: MarketTradeDateToken,
79 days: Vec<TradingDay>,
80 ) -> bool {
81 if key.market_id != token.market_id {
82 return false;
83 }
84 let mut order = self.security_order.lock();
85 if !self.security.contains_key(&key) {
86 while order.len() >= SECURITY_CACHE_CAPACITY {
87 if let Some(evicted) = order.pop_front() {
88 self.security.remove(&evicted);
89 }
90 }
91 order.push_back(key.clone());
92 }
93 self.security.insert(key, CacheEntry { token, days });
94 true
95 }
96
97 #[must_use]
98 pub fn market(
99 &self,
100 key: &MarketTradingSessionCacheKey,
101 current_token: MarketTradeDateToken,
102 ) -> Option<Vec<TradingDay>> {
103 let entry = self.market.get(key)?;
104 if u32::try_from(key.market_id).ok() != Some(current_token.market_id)
105 || entry.token != current_token
106 {
107 drop(entry);
108 self.market.remove(key);
109 return None;
110 }
111 Some(entry.days.clone())
112 }
113
114 pub fn insert_market_if_current(
115 &self,
116 key: MarketTradingSessionCacheKey,
117 token: MarketTradeDateToken,
118 days: Vec<TradingDay>,
119 ) -> bool {
120 if u32::try_from(key.market_id).ok() != Some(token.market_id) {
121 return false;
122 }
123 let mut order = self.market_order.lock();
124 if !self.market.contains_key(&key) {
125 while order.len() >= MARKET_CACHE_CAPACITY {
126 if let Some(evicted) = order.pop_front() {
127 self.market.remove(&evicted);
128 }
129 }
130 order.push_back(key.clone());
131 }
132 self.market.insert(key, CacheEntry { token, days });
133 true
134 }
135
136 pub fn invalidate_market(&self, market_id: u32) {
137 self.security.retain(|key, _| key.market_id != market_id);
138 if let Ok(market_id) = i32::try_from(market_id) {
139 self.market.retain(|key, _| key.market_id != market_id);
140 }
141 self.security_order
142 .lock()
143 .retain(|key| key.market_id != market_id);
144 if let Ok(market_id) = i32::try_from(market_id) {
145 self.market_order
146 .lock()
147 .retain(|key| key.market_id != market_id);
148 }
149 }
150
151 #[must_use]
152 pub fn entry_counts(&self) -> (usize, usize) {
153 (self.security.len(), self.market.len())
154 }
155}