Skip to main content

hypercall_vol_oracle/
risk_oracle.rs

1use std::fmt;
2use std::sync::Arc;
3
4use serde::{Deserialize, Serialize};
5
6use super::vol_surface_cache::{DeltaCurveExport, VolPoint};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum VolProviderKind {
10    BlockScholes,
11    Databento,
12    Deribit,
13    Derive,
14    Polygon,
15    Polymarket,
16    RealizedVol,
17    StickyMoneyness,
18    Fixed,
19}
20
21impl VolProviderKind {
22    pub fn as_str(&self) -> &'static str {
23        match self {
24            Self::BlockScholes => "blockscholes",
25            Self::Databento => "databento",
26            Self::Deribit => "deribit",
27            Self::Derive => "derive",
28            Self::Polygon => "polygon",
29            Self::Polymarket => "polymarket",
30            Self::RealizedVol => "realized_vol",
31            Self::StickyMoneyness => "sticky_moneyness",
32            Self::Fixed => "fixed",
33        }
34    }
35}
36
37#[derive(Debug, Clone)]
38pub enum VolLookupError {
39    UnsupportedUnderlying {
40        underlying: String,
41    },
42    UnhealthyProvider {
43        underlying: String,
44        provider: VolProviderKind,
45        reason: String,
46    },
47    MissingSurface {
48        underlying: String,
49        provider: VolProviderKind,
50        strike: f64,
51        expiry_ts: i64,
52    },
53    StaleSurface {
54        underlying: String,
55        provider: VolProviderKind,
56        staleness_seconds: f64,
57        threshold_seconds: f64,
58    },
59}
60
61impl fmt::Display for VolLookupError {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        match self {
64            Self::UnsupportedUnderlying { underlying } => {
65                write!(
66                    f,
67                    "No volatility provider configured for underlying {underlying}"
68                )
69            }
70            Self::UnhealthyProvider {
71                underlying,
72                provider,
73                reason,
74            } => write!(
75                f,
76                "Volatility provider {} is unhealthy for {}: {}",
77                provider.as_str(),
78                underlying,
79                reason
80            ),
81            Self::MissingSurface {
82                underlying,
83                provider,
84                strike,
85                expiry_ts,
86            } => write!(
87                f,
88                "Missing volatility surface for {} via {} at strike {} expiry {}",
89                underlying,
90                provider.as_str(),
91                strike,
92                expiry_ts
93            ),
94            Self::StaleSurface {
95                underlying,
96                provider,
97                staleness_seconds,
98                threshold_seconds,
99            } => write!(
100                f,
101                "Stale volatility surface for {} via {}: {:.1}s old (threshold {:.1}s)",
102                underlying,
103                provider.as_str(),
104                staleness_seconds,
105                threshold_seconds
106            ),
107        }
108    }
109}
110
111impl std::error::Error for VolLookupError {}
112
113#[derive(Debug, Clone)]
114pub struct VolOracleStatus {
115    pub underlying: String,
116    pub provider: VolProviderKind,
117    pub route_facing: bool,
118    pub connected: bool,
119    pub ready: bool,
120    pub last_update_ts_ms: Option<i64>,
121    pub staleness_seconds: Option<f64>,
122    pub staleness_threshold_seconds: Option<f64>,
123    pub surface_points: usize,
124    pub messages_received: u64,
125    pub last_error: Option<String>,
126}
127
128/// Full snapshot of a volatility surface for a single underlying, used for
129/// persistence and API serialisation.
130#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
131pub struct VolSurfaceSnapshot {
132    pub underlying: String,
133    pub last_update_ts_ms: Option<i64>,
134    pub expiries: Vec<i64>,
135    pub strike_points: Vec<VolPoint>,
136    pub delta_curves: Vec<DeltaCurveExport>,
137    pub atm_vols: Vec<(i64, f64)>,
138    pub spot_price: Option<f64>,
139}
140
141pub trait RiskVolOracle: Send + Sync {
142    fn get_iv(&self, underlying: &str, strike: f64, expiry_ts: i64) -> Result<f64, VolLookupError>;
143    fn statuses(&self) -> Vec<VolOracleStatus>;
144
145    /// Return a full snapshot of the vol surface for the given underlying.
146    fn get_surface_snapshot(&self, _underlying: &str) -> Option<VolSurfaceSnapshot> {
147        None
148    }
149
150    /// Whether this oracle can produce surface snapshots once its feeds are ready.
151    ///
152    /// This is capability, not current readiness. Startup wiring must not depend
153    /// on whether a feed has already populated a surface at builder time.
154    fn supports_surface_snapshots(&self) -> bool {
155        false
156    }
157}
158
159pub type SharedRiskVolOracle = Arc<dyn RiskVolOracle>;
160pub type SharedVolOracle = SharedRiskVolOracle;