1use std::collections::BTreeMap;
2use std::sync::Arc;
3use std::time::{Duration, Instant};
4
5use arc_swap::ArcSwap;
6use dashmap::DashMap;
7use futu_domain_qot_event_contract::{
8 Category, CategoryGeneration, Competition, ContractExtra, EventExtra, MetadataGeneration,
9 MilestoneExtra, RelatedEvent, Scope, SecurityExtra, SecurityMetadata, SeriesExtra,
10 SubContractRelation, Tag,
11};
12use futu_proto_internal::event_contract::EventContract;
13use futu_proto_internal::ft_string_define::LocaleString;
14use futu_proto_internal::quote_event_contract_svr::{
15 GetCompetitionMixListResponse, GetEventCategoryResponse, GetMvcInfoResponse,
16};
17use futu_proto_internal::stock_information_svr::{
18 GetEventContractExtraInfoRsp, GetEventContractRelationRsp, SecuritiesRsp,
19};
20use parking_lot::Mutex;
21use prost::Message;
22
23mod catalog;
24mod metadata;
25mod runtime;
26
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub enum EventContractCacheError {
29 Decode(String),
30 Validation(&'static str),
31}
32
33#[derive(Debug, Clone, Copy, Default, PartialEq)]
34pub struct EventContractOrderBookLevel {
35 pub price: f64,
36 pub size: f64,
37}
38
39#[derive(Debug, Clone, Default, PartialEq)]
40pub struct EventContractOrderBook {
41 pub yes_bids: Vec<EventContractOrderBookLevel>,
42 pub yes_asks: Vec<EventContractOrderBookLevel>,
43 pub no_bids: Vec<EventContractOrderBookLevel>,
44 pub no_asks: Vec<EventContractOrderBookLevel>,
45}
46
47#[derive(Debug, Clone, Default, PartialEq)]
48pub struct EventContractOrderBookGeneration {
49 pub generation: u64,
50 pub book: EventContractOrderBook,
51 yes_generation: Option<u64>,
52 no_generation: Option<u64>,
53}
54
55impl EventContractOrderBookGeneration {
56 #[must_use]
57 pub fn is_complete(&self) -> bool {
58 self.yes_generation.is_some() && self.no_generation.is_some()
59 }
60}
61
62#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
63pub struct EventContractMvcEventRule {
64 pub event_id: u64,
65 pub yes_only: bool,
66 pub min_contract_size: i32,
67 pub max_contract_size: i32,
68}
69
70#[derive(Debug, Clone, Default, PartialEq, Eq)]
71pub struct EventContractMvcRule {
72 pub min_contract_size: i32,
73 pub max_contract_size: i32,
74 pub events: Vec<EventContractMvcEventRule>,
75}
76
77#[derive(Debug, Clone, PartialEq, Eq)]
78struct EventContractRfqQuoteEntry {
79 written_at: Instant,
80 legs: Vec<(u64, i32)>,
81 combo_exchange: String,
82 combo_origin_symbol: String,
83}
84
85pub type EventContractRfqQuoteState = (Instant, Vec<(u64, i32)>, String, String);
86
87const RFQ_QUOTE_VALIDITY: Duration = Duration::from_millis(5_000);
91const RFQ_QUOTE_STALE_AFTER: Duration = Duration::from_millis(60_000);
92
93pub struct EventContractCache {
94 category: ArcSwap<CategoryGeneration>,
95 metadata: ArcSwap<MetadataGeneration>,
96 mvc_rules: ArcSwap<BTreeMap<String, EventContractMvcRule>>,
97 rfq_quotes: ArcSwap<BTreeMap<String, EventContractRfqQuoteEntry>>,
98 order_books: DashMap<u64, Arc<EventContractOrderBookGeneration>>,
99 category_writer: Mutex<()>,
100 metadata_writer: Mutex<()>,
101 mvc_writer: Mutex<()>,
102 rfq_quote_writer: Mutex<()>,
103}
104
105impl EventContractCache {
106 #[must_use]
107 pub fn new() -> Arc<Self> {
108 Arc::new(Self {
109 category: ArcSwap::from_pointee(CategoryGeneration::default()),
110 metadata: ArcSwap::from_pointee(MetadataGeneration::default()),
111 mvc_rules: ArcSwap::from_pointee(BTreeMap::new()),
112 rfq_quotes: ArcSwap::from_pointee(BTreeMap::new()),
113 order_books: DashMap::new(),
114 category_writer: Mutex::new(()),
115 metadata_writer: Mutex::new(()),
116 mvc_writer: Mutex::new(()),
117 rfq_quote_writer: Mutex::new(()),
118 })
119 }
120
121 #[must_use]
122 pub fn category_snapshot(&self) -> Arc<CategoryGeneration> {
123 self.category.load_full()
124 }
125
126 #[must_use]
127 pub fn metadata_snapshot(&self) -> Arc<MetadataGeneration> {
128 self.metadata.load_full()
129 }
130}
131
132impl Default for EventContractCache {
133 fn default() -> Self {
134 Self {
135 category: ArcSwap::from_pointee(CategoryGeneration::default()),
136 metadata: ArcSwap::from_pointee(MetadataGeneration::default()),
137 mvc_rules: ArcSwap::from_pointee(BTreeMap::new()),
138 rfq_quotes: ArcSwap::from_pointee(BTreeMap::new()),
139 order_books: DashMap::new(),
140 category_writer: Mutex::new(()),
141 metadata_writer: Mutex::new(()),
142 mvc_writer: Mutex::new(()),
143 rfq_quote_writer: Mutex::new(()),
144 }
145 }
146}
147
148#[cfg(test)]
149mod tests;