Skip to main content

hypercall/rsm/
engine_vol_oracle.rs

1use crate::vol_oracle::risk_oracle::{
2    RiskVolOracle, VolLookupError, VolOracleStatus, VolProviderKind, VolSurfaceSnapshot,
3};
4use crate::vol_oracle::vol_surface_cache::VolatilitySurface;
5use std::collections::HashMap;
6use std::sync::{Arc, RwLock};
7
8/// Vol oracle backed exclusively by engine-internal IV surfaces.
9///
10/// Updated by `IvUpdate` engine commands. If no surface exists for an
11/// underlying, the lookup fails closed (UnsupportedUnderlying). This
12/// ensures margin checks are deterministic and only use data that
13/// arrived through the command stream.
14pub struct EngineVolOracle {
15    surfaces: Arc<RwLock<HashMap<String, VolatilitySurface>>>,
16}
17
18impl EngineVolOracle {
19    pub fn new(surfaces: Arc<RwLock<HashMap<String, VolatilitySurface>>>) -> Self {
20        Self { surfaces }
21    }
22}
23
24impl RiskVolOracle for EngineVolOracle {
25    fn get_iv(&self, underlying: &str, strike: f64, expiry_ts: i64) -> Result<f64, VolLookupError> {
26        let surfaces = self
27            .surfaces
28            .read()
29            .expect("engine vol oracle lock poisoned");
30
31        let surface =
32            surfaces
33                .get(underlying)
34                .ok_or_else(|| VolLookupError::UnsupportedUnderlying {
35                    underlying: underlying.to_string(),
36                })?;
37
38        surface
39            .get_interpolated(strike, expiry_ts)
40            .ok_or_else(|| VolLookupError::MissingSurface {
41                underlying: underlying.to_string(),
42                provider: VolProviderKind::Fixed,
43                strike,
44                expiry_ts,
45            })
46    }
47
48    fn statuses(&self) -> Vec<VolOracleStatus> {
49        let surfaces = self
50            .surfaces
51            .read()
52            .expect("engine vol oracle lock poisoned");
53        surfaces
54            .keys()
55            .map(|underlying| VolOracleStatus {
56                underlying: underlying.clone(),
57                provider: VolProviderKind::Fixed,
58                route_facing: true,
59                connected: true,
60                ready: true,
61                last_update_ts_ms: None,
62                staleness_seconds: Some(0.0),
63                staleness_threshold_seconds: None,
64                surface_points: 0,
65                messages_received: 0,
66                last_error: None,
67            })
68            .collect()
69    }
70
71    fn get_surface_snapshot(&self, _underlying: &str) -> Option<VolSurfaceSnapshot> {
72        None
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn missing_underlying_fails_closed() {
82        let oracle = EngineVolOracle::new(Arc::new(RwLock::new(HashMap::new())));
83
84        assert!(matches!(
85            oracle.get_iv("GOLD", 4700.0, 1_800_000_000),
86            Err(VolLookupError::UnsupportedUnderlying { .. })
87        ));
88    }
89
90    #[test]
91    fn missing_surface_point_returns_missing_surface() {
92        let mut surfaces = HashMap::new();
93        surfaces.insert("GOLD".to_string(), VolatilitySurface::new());
94        let oracle = EngineVolOracle::new(Arc::new(RwLock::new(surfaces)));
95
96        assert!(matches!(
97            oracle.get_iv("GOLD", 4700.0, 1_800_000_000),
98            Err(VolLookupError::MissingSurface { .. })
99        ));
100    }
101}