futu_cache/
option_chain_statistic.rs1use std::collections::HashMap;
9use std::sync::Arc;
10
11use parking_lot::RwLock;
12
13#[derive(Debug, Clone, Default, PartialEq, Eq)]
14pub struct OptionChainStatisticSnapshot {
15 pub strike_date_iv_1e5: HashMap<i64, i64>,
16 pub underlying_iv_1e5: Option<i64>,
17 pub underlying_hv_1e5: Option<i64>,
18}
19
20#[derive(Debug, Default)]
21pub struct OptionChainStatisticCache {
22 by_underlying: RwLock<HashMap<u64, OptionChainStatisticSnapshot>>,
23}
24
25impl OptionChainStatisticCache {
26 #[must_use]
27 pub fn new() -> Arc<Self> {
28 Arc::new(Self::default())
29 }
30
31 pub fn replace_underlying(
32 &self,
33 underlying_stock_id: u64,
34 snapshot: OptionChainStatisticSnapshot,
35 ) {
36 if underlying_stock_id == 0 {
37 return;
38 }
39 self.by_underlying
40 .write()
41 .insert(underlying_stock_id, snapshot);
42 }
43
44 #[must_use]
45 pub fn snapshot(&self, underlying_stock_id: u64) -> Option<OptionChainStatisticSnapshot> {
46 if underlying_stock_id == 0 {
47 return None;
48 }
49 self.by_underlying.read().get(&underlying_stock_id).cloned()
50 }
51
52 #[must_use]
53 pub fn strike_date_iv_decimal(
54 &self,
55 underlying_stock_id: u64,
56 strike_date_sec: i64,
57 ) -> Option<f64> {
58 let raw = self
59 .snapshot(underlying_stock_id)?
60 .strike_date_iv_1e5
61 .get(&strike_date_sec)
62 .copied()?;
63 (raw != 0).then(|| iv_1e5_to_decimal(raw))
64 }
65
66 #[must_use]
67 pub fn underlying_iv_decimal(&self, underlying_stock_id: u64) -> Option<f64> {
68 self.snapshot(underlying_stock_id)?
69 .underlying_iv_1e5
70 .map(iv_1e5_to_decimal)
71 }
72
73 #[must_use]
74 pub fn underlying_hv_decimal(&self, underlying_stock_id: u64) -> Option<f64> {
75 self.snapshot(underlying_stock_id)?
76 .underlying_hv_1e5
77 .map(iv_1e5_to_decimal)
78 }
79}
80
81#[must_use]
82pub fn iv_1e5_to_decimal(raw: i64) -> f64 {
83 raw as f64 / 100_000.0
84}
85
86#[cfg(test)]
87mod tests;