pub struct Surface { /* private fields */ }Expand description
An arbitrage-checked volatility surface.
Built from either a set of calibrated raw slices (Surface::from_slices)
or an SSVI parametrisation (Surface::from_ssvi). Evaluation at any
(k, T) returns total variance (Surface::total_variance) or implied
volatility (Surface::implied_vol).
§Examples
use regit_svi::raw::RawSvi;
use regit_svi::surface::Surface;
let s1 = RawSvi::new(0.02, 0.3, -0.2, 0.0, 0.1).unwrap();
let s2 = RawSvi::new(0.05, 0.3, -0.2, 0.0, 0.1).unwrap();
let surface = Surface::from_slices(vec![(0.5, s1), (1.5, s2)]).unwrap();
// Total variance at an interpolated maturity.
let w = surface.total_variance(0.0, 1.0);
assert!(w > s1.total_variance(0.0) && w < s2.total_variance(0.0));Implementations§
Source§impl Surface
impl Surface
Sourcepub fn from_slices(slices: Vec<(f64, RawSvi)>) -> Result<Self, ParamError>
pub fn from_slices(slices: Vec<(f64, RawSvi)>) -> Result<Self, ParamError>
Builds a surface from (maturity, slice) pairs.
The pairs are sorted into ascending maturity order. Maturities must be strictly positive and distinct.
§Errors
ParamError::NonFiniteif a maturity is not finite.ParamError::NonPositiveMaturityif a maturity is<= 0or two maturities coincide.
§Examples
use regit_svi::raw::RawSvi;
use regit_svi::surface::Surface;
let s = RawSvi::new(0.04, 0.3, -0.2, 0.0, 0.1).unwrap();
assert!(Surface::from_slices(vec![(1.0, s)]).is_ok());
assert!(Surface::from_slices(vec![(0.0, s)]).is_err());Sourcepub fn from_ssvi(ssvi: Ssvi, term: Vec<(f64, f64)>) -> Result<Self, ParamError>
pub fn from_ssvi(ssvi: Ssvi, term: Vec<(f64, f64)>) -> Result<Self, ParamError>
Builds a surface from an SSVI parametrisation and its (maturity, theta) term structure.
The term structure is sorted into ascending maturity order; theta
must be non-decreasing (a non-decreasing ATM term structure is one of
the SSVI no-calendar conditions).
§Errors
ParamError::NonPositiveMaturityif the term structure is empty or contains a non-positive / duplicate maturity.ParamError::NonPositiveThetaif athetais non-positive.ParamError::NonFiniteif a knot is not finite.
§Examples
use regit_svi::ssvi::{Phi, Ssvi};
use regit_svi::surface::Surface;
let ssvi = Ssvi::new(-0.3, Phi::power_law(0.5, 0.5).unwrap()).unwrap();
let surface = Surface::from_ssvi(ssvi, vec![(0.5, 0.02), (1.0, 0.04)]).unwrap();
assert!(surface.total_variance(0.0, 0.75) > 0.0);Sourcepub fn len(&self) -> usize
pub fn len(&self) -> usize
The number of maturity knots in the surface.
§Examples
use regit_svi::raw::RawSvi;
use regit_svi::surface::Surface;
let s = RawSvi::new(0.04, 0.3, -0.2, 0.0, 0.1).unwrap();
let surface = Surface::from_slices(vec![(0.5, s), (1.0, s)]).unwrap();
assert_eq!(surface.len(), 2);Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
Returns true if the surface has no maturity knots.
A Surface is always constructed with at least one knot, so this
returns false for every value built through the public constructors.
§Examples
use regit_svi::raw::RawSvi;
use regit_svi::surface::Surface;
let s = RawSvi::new(0.04, 0.3, -0.2, 0.0, 0.1).unwrap();
let surface = Surface::from_slices(vec![(1.0, s)]).unwrap();
assert!(!surface.is_empty());Sourcepub fn total_variance(&self, k: f64, t: f64) -> f64
pub fn total_variance(&self, k: f64, t: f64) -> f64
Total implied variance w(k, T) at log-moneyness k and maturity T.
Inside the maturity grid the value is linearly interpolated in total
variance; outside it the implied volatility is held flat (constant
sigma_BS). An SSVI-backed surface evaluates the closed form at the
interpolated theta.
§Examples
use regit_svi::raw::RawSvi;
use regit_svi::surface::Surface;
let s1 = RawSvi::new(0.02, 0.3, -0.2, 0.0, 0.1).unwrap();
let s2 = RawSvi::new(0.05, 0.3, -0.2, 0.0, 0.1).unwrap();
let surface = Surface::from_slices(vec![(0.5, s1), (1.5, s2)]).unwrap();
// Midpoint maturity -> midpoint total variance at constant k.
let mid = surface.total_variance(0.0, 1.0);
let expect = 0.5 * (s1.total_variance(0.0) + s2.total_variance(0.0));
assert!((mid - expect).abs() < 1e-12);Sourcepub fn implied_vol(&self, k: f64, t: f64) -> Result<f64, ParamError>
pub fn implied_vol(&self, k: f64, t: f64) -> Result<f64, ParamError>
Black implied volatility sigma_BS(k, T) = sqrt(w(k, T) / T).
§Errors
Returns ParamError::NonPositiveMaturity if T <= 0.
§Examples
use regit_svi::raw::RawSvi;
use regit_svi::surface::Surface;
let s = RawSvi::new(0.04, 0.0, 0.0, 0.0, 0.1).unwrap();
let surface = Surface::from_slices(vec![(1.0, s)]).unwrap();
// Flat w = 0.04 at t = 1 -> vol = 0.2.
assert!((surface.implied_vol(0.0, 1.0).unwrap() - 0.2).abs() < 1e-12);Sourcepub fn is_calendar_free(&self, k_lo: f64, k_hi: f64) -> bool
pub fn is_calendar_free(&self, k_lo: f64, k_hi: f64) -> bool
Checks the surface for calendar-spread arbitrage across adjacent maturity knots.
For a slice-backed surface, runs the calendar_scan on every
adjacent pair over [k_lo, k_hi]; the surface is calendar-free if
every pair is. For an SSVI-backed surface, the closed-form Theorem 4.1
conditions are used.
§Examples
use regit_svi::raw::RawSvi;
use regit_svi::surface::Surface;
let s1 = RawSvi::new(0.02, 0.3, -0.2, 0.0, 0.1).unwrap();
let s2 = RawSvi::new(0.05, 0.3, -0.2, 0.0, 0.1).unwrap();
let surface = Surface::from_slices(vec![(0.5, s1), (1.5, s2)]).unwrap();
assert!(surface.is_calendar_free(-0.5, 0.5));