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#[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 fn get_surface_snapshot(&self, _underlying: &str) -> Option<VolSurfaceSnapshot> {
147 None
148 }
149
150 fn supports_surface_snapshots(&self) -> bool {
155 false
156 }
157}
158
159pub type SharedRiskVolOracle = Arc<dyn RiskVolOracle>;
160pub type SharedVolOracle = SharedRiskVolOracle;