pub struct VolatilitySurface {
points: HashMap<i64, HashMap<i64, VolPoint>>,
atm_vols: HashMap<i64, f64>,
delta_curves: HashMap<i64, Vec<DeltaIvPoint>>,
strike_precision: f64,
last_update: Option<i64>,
expiries: BTreeSet<i64>,
}Expand description
Volatility surface for a single underlying.
Supports two data representations:
- Strike-space: direct (strike, expiry) → IV grid with bilinear interpolation
- Delta-space: (delta, expiry) → IV curves, queried via strike→delta conversion
When both are available, strike-space is preferred (exact). When only delta-space data exists (e.g., from Block Scholes delta.iv feed), the caller provides spot price to convert query strikes to deltas for interpolation.
Fields§
§points: HashMap<i64, HashMap<i64, VolPoint>>Map from quantized_strike -> expiry -> VolPoint
atm_vols: HashMap<i64, f64>ATM volatilities by expiry for quick ATM lookups
delta_curves: HashMap<i64, Vec<DeltaIvPoint>>Delta-IV curves by expiry, sorted by delta ascending
strike_precision: f64Strike quantization factor (e.g., 100 = $100 precision)
last_update: Option<i64>Last update time for the entire surface (milliseconds)
expiries: BTreeSet<i64>All known expiries for iteration
Implementations§
Source§impl VolatilitySurface
impl VolatilitySurface
Sourcepub fn with_precision(strike_precision: f64) -> Self
pub fn with_precision(strike_precision: f64) -> Self
Create a new volatility surface with custom strike precision.
§Arguments
strike_precision- Strike price bucket size (e.g., 1.0 for $1 buckets)
Sourcefn quantize_strike(&self, strike: f64) -> i64
fn quantize_strike(&self, strike: f64) -> i64
Quantize strike to avoid floating point comparison issues.
Sourcepub fn insert(&mut self, strike: f64, expiry: i64, iv: f64)
pub fn insert(&mut self, strike: f64, expiry: i64, iv: f64)
Insert or update a volatility point.
Sourcepub fn set_atm_vol(&mut self, expiry: i64, iv: f64)
pub fn set_atm_vol(&mut self, expiry: i64, iv: f64)
Set ATM volatility for an expiry.
Sourcepub fn set_delta_iv(&mut self, expiry: i64, delta: f64, iv: f64)
pub fn set_delta_iv(&mut self, expiry: i64, delta: f64, iv: f64)
Store a delta-IV point for an expiry.
Delta-IV curves are used when strike-level data is unavailable. The caller provides spot price at query time to convert strikes to deltas.
Sourcepub fn get(&self, strike: f64, expiry: i64) -> Option<f64>
pub fn get(&self, strike: f64, expiry: i64) -> Option<f64>
Get volatility at exact (strike, expiry) or None.
Sourcepub fn get_point(&self, strike: f64, expiry: i64) -> Option<&VolPoint>
pub fn get_point(&self, strike: f64, expiry: i64) -> Option<&VolPoint>
Get the full VolPoint at exact (strike, expiry) or None.
Sourcepub fn get_interpolated_with_spot(
&self,
strike: f64,
expiry: i64,
spot: Option<f64>,
) -> Option<f64>
pub fn get_interpolated_with_spot( &self, strike: f64, expiry: i64, spot: Option<f64>, ) -> Option<f64>
Get volatility with interpolation if exact match not found.
Uses bilinear interpolation across strike and expiry dimensions.
If spot is provided and no strike-level data exists, falls back to
delta-space interpolation: converts the query strike to a call delta
and interpolates the delta-IV smile, then interpolates across expiries.
Sourcepub fn get_interpolated(&self, strike: f64, expiry: i64) -> Option<f64>
pub fn get_interpolated(&self, strike: f64, expiry: i64) -> Option<f64>
Get volatility with interpolation (no spot price for delta conversion).
Sourcefn interpolate_delta_surface(
&self,
strike: f64,
expiry: i64,
spot: f64,
) -> Option<f64>
fn interpolate_delta_surface( &self, strike: f64, expiry: i64, spot: f64, ) -> Option<f64>
Interpolate the delta-IV surface for a given strike and expiry.
Converts strike to call delta using approximate ATM vol, then linearly interpolates the delta-IV smile at that delta. Finally interpolates across expiries.
Sourcefn interpolate_delta_smile_at_expiry(
&self,
strike: f64,
expiry: i64,
spot: f64,
) -> Option<f64>
fn interpolate_delta_smile_at_expiry( &self, strike: f64, expiry: i64, spot: f64, ) -> Option<f64>
Interpolate the delta-IV smile at a single expiry for a given strike.
Sourcepub fn last_update(&self) -> Option<i64>
pub fn last_update(&self) -> Option<i64>
Get the last update timestamp.
Sourcepub fn export_all_points(&self) -> Vec<VolPoint>
pub fn export_all_points(&self) -> Vec<VolPoint>
Export all strike-space points for serialisation.
Sourcepub fn export_delta_curves(&self) -> Vec<DeltaCurveExport>
pub fn export_delta_curves(&self) -> Vec<DeltaCurveExport>
Export delta-IV curves for every expiry.
Sourcepub fn export_atm_vols(&self) -> Vec<(i64, f64)>
pub fn export_atm_vols(&self) -> Vec<(i64, f64)>
Export ATM vols as (expiry, iv) pairs.
Sourcepub fn sanitize_arb_free(&mut self, spot: f64, risk_free_rate: f64) -> u32
pub fn sanitize_arb_free(&mut self, spot: f64, risk_free_rate: f64) -> u32
Enforce static no-arbitrage constraints on the strike-indexed surface.
For each expiry, walks strikes ascending and clamps IVs down so that:
- Call prices are non-increasing in K (no vertical-spread arb)
- Call prices are convex in K (no butterfly arb)
Both constraints follow from risk-neutral pricing; they must hold for any arb-free surface. We enforce in price space (via Black-Scholes) then back out the clamped IV by bisection — this handles non-uniform strike grids correctly and uses the same pricer the rest of the system trusts.
Only clamps down: if an IV is lower than it should be for arb-freeness, we do nothing. Lowering an IV cannot create new arbs against neighbors we have already cleaned.
Returns the number of IV points modified. Pass risk_free_rate = 0.0 on
testnet.
Sourcefn update_iv(&mut self, quantized_strike: i64, expiry: i64, new_iv: f64)
fn update_iv(&mut self, quantized_strike: i64, expiry: i64, new_iv: f64)
Mutate the stored IV for an already-present (quantized_strike, expiry).
Sourcefn find_bracketing_values(
&self,
target: i64,
keys: &[i64],
) -> Option<(i64, i64)>
fn find_bracketing_values( &self, target: i64, keys: &[i64], ) -> Option<(i64, i64)>
Find bracketing values for interpolation.
Sourcefn bilinear_interpolate(&self, point: BilinearPoint) -> Option<f64>
fn bilinear_interpolate(&self, point: BilinearPoint) -> Option<f64>
Perform bilinear interpolation on the four corner values.
Trait Implementations§
Source§impl Clone for VolatilitySurface
impl Clone for VolatilitySurface
Source§fn clone(&self) -> VolatilitySurface
fn clone(&self) -> VolatilitySurface
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for VolatilitySurface
impl Debug for VolatilitySurface
Auto Trait Implementations§
impl Freeze for VolatilitySurface
impl RefUnwindSafe for VolatilitySurface
impl Send for VolatilitySurface
impl Sync for VolatilitySurface
impl Unpin for VolatilitySurface
impl UnsafeUnpin for VolatilitySurface
impl UnwindSafe for VolatilitySurface
Blanket Implementations§
§impl<T> AggregateExpressionMethods for T
impl<T> AggregateExpressionMethods for T
§fn aggregate_distinct(self) -> Self::Outputwhere
Self: DistinctDsl,
fn aggregate_distinct(self) -> Self::Outputwhere
Self: DistinctDsl,
DISTINCT modifier for aggregate functions Read more§fn aggregate_all(self) -> Self::Outputwhere
Self: AllDsl,
fn aggregate_all(self) -> Self::Outputwhere
Self: AllDsl,
ALL modifier for aggregate functions Read more§fn aggregate_filter<P>(self, f: P) -> Self::Outputwhere
P: AsExpression<Bool>,
Self: FilterDsl<<P as AsExpression<Bool>>::Expression>,
fn aggregate_filter<P>(self, f: P) -> Self::Outputwhere
P: AsExpression<Bool>,
Self: FilterDsl<<P as AsExpression<Bool>>::Expression>,
§fn aggregate_order<O>(self, o: O) -> Self::Outputwhere
Self: OrderAggregateDsl<O>,
fn aggregate_order<O>(self, o: O) -> Self::Outputwhere
Self: OrderAggregateDsl<O>,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Conv for T
impl<T> Conv for T
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.§impl<T> DowncastSend for T
impl<T> DowncastSend for T
§impl<T> DowncastSync for T
impl<T> DowncastSync for T
§impl<T> FmtForward for T
impl<T> FmtForward for T
§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.§fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more§impl<T> IntoSql for T
impl<T> IntoSql for T
§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read more§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read more§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
§impl<T> Tap for T
impl<T> Tap for T
§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read more§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read more§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read more§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read more§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read more§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read more§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.