### Install volsurf-rs Python Bindings Source: https://github.com/volsurf-rs/volsurf/blob/main/README.md Installs the volsurf-rs Python bindings using pip. This command assumes you have Python and pip installed. ```bash pip install volsurf ``` -------------------------------- ### Quick Start: Initialize and Use WasmSviSmile Source: https://github.com/volsurf-rs/volsurf/blob/main/wasm/README.md Initializes the WASM module and demonstrates constructing an SVI smile directly from parameters and calibrating one from market data. ```typescript import init, { WasmSviSmile, WasmSurfaceBuilder } from "volsurf-wasm"; await init(); // Construct an SVI smile directly from parameters const smile = new WasmSviSmile(100.0, 1.0, 0.04, 0.4, -0.4, 0.0, 0.1); console.log(smile.vol(100.0)); // ATM implied vol // Calibrate from market data (flattened strike/vol pairs) const calibrated = WasmSviSmile.calibrate(100.0, 1.0, [ 80, 0.28, 90, 0.24, 95, 0.22, 100, 0.20, 105, 0.22, 110, 0.24, 120, 0.28, ]); console.log(calibrated.vol(100.0)); ``` -------------------------------- ### Browser Usage Example Source: https://github.com/volsurf-rs/volsurf/blob/main/wasm/README.md An example of how to import and use WasmSviSmile in a web browser using a module script. ```html ``` -------------------------------- ### Configure Calibration with DataFilter and WeightingScheme Source: https://context7.com/volsurf-rs/volsurf/llms.txt Configure calibration by filtering market data and choosing a weighting scheme. DataFilter removes illiquid strikes, while WeightingScheme controls how residuals are weighted (e.g., Vega, Uniform). ```rust use volsurf::smile::{SabrSmile, SviSmile, SmileSection}; use volsurf::calibration::{DataFilter, WeightingScheme}; use volsurf::types::Strike; // Market data including illiquid deep wings let market: Vec<(f64, f64)> = vec![ (20.0, 0.90), // deep ITM — illiquid (80.0, 0.28), (90.0, 0.24), (100.0, 0.20), (110.0, 0.22), (120.0, 0.26), (300.0, 0.80), // deep OTM — illiquid ]; // Filter to |ln(K/F)| < 0.3, ignore illiquid wings let filter = DataFilter { max_log_moneyness: Some(0.30), min_vol: Some(0.05), vol_cliff_filter: Some(true), }; // SVI with vega weighting (literature default) let svi = SviSmile::calibrate_with_config( 100.0, 1.0, &market, &filter, &WeightingScheme::Vega, )?; println!("SVI ATM vol: {:.4}%", svi.vol(Strike(100.0))?.0 * 100.0); // SABR with uniform weighting (Hagan convention) let sabr = SabrSmile::calibrate_with_config( 100.0, 1.0, 0.5, &market, &filter, &WeightingScheme::Uniform, None, // no warm-start seed )?; println!("SABR ATM vol: {:.4}%", sabr.vol(Strike(100.0))?.0 * 100.0); # Ok::<(), volsurf::VolSurfError>(()) ``` -------------------------------- ### WasmSviSmile API: Construction and Calibration Source: https://github.com/volsurf-rs/volsurf/blob/main/wasm/README.md Demonstrates constructing a WasmSviSmile from parameters or calibrating it from market data. Requires at least 5 market data pairs. ```typescript // From parameters const svi = new WasmSviSmile(forward, expiry, a, b, rho, m, sigma); // From market data: [strike1, vol1, strike2, vol2, ...] (min 5 pairs) const svi = WasmSviSmile.calibrate(forward, expiry, marketVolsFlat); ``` -------------------------------- ### Construct and Use SsviSurface Source: https://context7.com/volsurf-rs/volsurf/llms.txt Construct an SSVI surface from global parameters and tenor-specific variances. Use it to query implied volatilities and perform arbitrage diagnostics. Requires specifying rho, eta, gamma, tenors, forward prices, and theta values. ```rust use volsurf::surface::{SsviSurface, VolSurface}; use volsurf::types::{Strike, Tenor}; // Construct from known parameters let surface = SsviSurface::new( -0.3, // rho: equity skew 0.5, // eta: smile amplitude 0.5, // gamma: term structure decay vec![0.25, 0.50, 1.0, 2.0], // tenors (years) vec![100.0; 4], // forward prices vec![0.01, 0.02, 0.04, 0.08], // theta_i = sigma_ATM^2 * T )?; let vol = surface.black_vol(Tenor(0.5), Strike(100.0))?; println!("ATM 6M vol: {:.4}%", vol.0 * 100.0); // Arbitrage diagnostics (butterfly + calendar) let diag = surface.diagnostics()?; println!("Surface arb-free: {}", diag.is_free()); let cal = surface.calendar_arb_analytical(); println!("Analytical calendar violations: {}", cal.len()); // Calibrate from multi-tenor market data let tenors = vec![0.25, 0.50, 1.0, 2.0]; let forwards = vec![100.0; 4]; let strikes_grid: Vec = (70..=130).step_by(5).map(|k| k as f64).collect(); let market_data: Vec> = tenors.iter() .map(|&t| strikes_grid.iter() .map(|&k| Ok((k, surface.black_vol(Tenor(t), Strike(k))?.0))) .collect::, volsurf::VolSurfError>>()) .collect::>()?; let calibrated = SsviSurface::calibrate(&market_data, &tenors, &forwards)?; println!("Calibrated: rho={:.4}, eta={:.4}, gamma={:.4}", calibrated.rho(), calibrated.eta(), calibrated.gamma()); # Ok::<(), volsurf::VolSurfError>(()) ``` -------------------------------- ### Construct and Calibrate SABR Smile Source: https://context7.com/volsurf-rs/volsurf/llms.txt Use SabrSmile::new for direct construction with equity convention (beta=0.5). Calibrate using SabrSmile::calibrate with market data, requiring a minimum of 4 points. Supports warm-starting for sequential tenor fitting. ```rust use volsurf::smile::{SabrSmile, SmileSection}; use volsurf::types::Strike; // Construct SABR smile — equity convention (beta=0.5) let smile = SabrSmile::new( 100.0, // forward 1.0, // expiry 0.3, // alpha: ATM vol scale 0.5, // beta: CIR backbone -0.3, // rho: negative equity skew 0.4, // nu: vol-of-vol (smile curvature) )?; println!("ATM vol: {:.4}%", smile.vol(Strike(100.0))?.0 * 100.0); println!("OTM call: {:.4}%", smile.vol(Strike(120.0))?.0 * 100.0); println!("OTM put: {:.4}%", smile.vol(Strike( 80.0))?.0 * 100.0); // Negative rho → put wing vol > call wing vol assert!(smile.vol(Strike(80.0))?.0 > smile.vol(Strike(120.0))?.0); // Calibrate from market data (minimum 4 points) let market: Vec<(f64, f64)> = vec![ (80.0, 0.28), (90.0, 0.24), (100.0, 0.20), (110.0, 0.22), (120.0, 0.26), ]; let calibrated = SabrSmile::calibrate(100.0, 1.0, 0.5 /* beta */, &market)?; println!("Calibrated alpha={:.4}, rho={:.4}, nu={:.4}", calibrated.alpha(), calibrated.rho(), calibrated.nu()); // Warm-start the next tenor from this calibration use volsurf::calibration::{DataFilter, WeightingScheme}; let next_tenor_market: Vec<(f64, f64)> = vec![ (80.0, 0.26), (90.0, 0.22), (100.0, 0.19), (110.0, 0.21), (120.0, 0.24), ]; let next = SabrSmile::calibrate_with_config( 100.0, 2.0, 0.5, &next_tenor_market, &DataFilter::default(), &WeightingScheme::Uniform, Some(&calibrated), // warm-start seed )?; println!("Warm-start result: alpha={:.4}", next.alpha()); # Ok::<(), volsurf::VolSurfError>(()) ``` -------------------------------- ### WasmSurfaceBuilder API: Adding Tenors and Building Source: https://github.com/volsurf-rs/volsurf/blob/main/wasm/README.md Demonstrates adding market data for different tenors and then building the piecewise surface. ```typescript builder.addTenor(0.25, strikes, vols); builder.addTenor(1.0, strikes, vols); const surface = builder.build(); // returns WasmPiecewiseSurface ``` -------------------------------- ### Construct and Calibrate SVI Smile Source: https://context7.com/volsurf-rs/volsurf/llms.txt Use SviSmile::new to construct a smile from parameters, enforcing no-arbitrage conditions. Calibrate using SviSmile::calibrate with market data. Verify fit quality and check for arbitrage violations. ```rust use volsurf::smile::{SviSmile, SmileSection}; use volsurf::types::Strike; // Construct from known parameters (enforces no-arb conditions) let svi = SviSmile::new( 100.0, // forward 1.0, // expiry (years) 0.04, // a: minimum variance level 0.10, // b: variance slope -0.3, // rho: skew direction 0.0, // m: moneyness shift 0.20, // sigma: smile curvature )?; // Query vol and density let vol = svi.vol(Strike(100.0))?; let den = svi.density(Strike(100.0))?; println!("ATM vol={:.4}%, density={:.6}", vol.0 * 100.0, den); // Calibrate from market (strike, implied_vol) pairs let market: Vec<(f64, f64)> = vec![ (70.0, 0.32), (80.0, 0.28), (90.0, 0.24), (95.0, 0.22), (100.0, 0.20), (105.0, 0.21), (110.0, 0.23), (120.0, 0.27), (130.0, 0.31), ]; let calibrated = SviSmile::calibrate(100.0, 1.0, &market)?; // Verify fit quality let rms: f64 = (market.iter() .map(|&(k, v)| (calibrated.vol(Strike(k))?.0 - v).powi(2)) .collect::, _>>()? .iter().sum::() / market.len() as f64).sqrt(); println!("SVI RMS vol error: {rms:.2e}"); // typically < 0.001 // Check butterfly arbitrage let report = calibrated.is_arbitrage_free()?; println!("Arb-free: {}, violations: {}", report.is_free(), report.butterfly_violations.len()); # Ok::<(), volsurf::VolSurfError>(()) ``` -------------------------------- ### Error Handling with try-catch Source: https://github.com/volsurf-rs/volsurf/blob/main/wasm/README.md Illustrates how to handle potential errors thrown by constructors or query methods using a try-catch block. ```typescript try { const smile = new WasmSviSmile(100, 1, 0.04, 0.4, -0.4, 0, 0.1); const vol = smile.vol(100); } catch (e) { console.error(e); // string describing the error } ``` -------------------------------- ### WasmSsviSurface API: Construction Source: https://github.com/volsurf-rs/volsurf/blob/main/wasm/README.md Demonstrates the construction of a global SSVI parameterization from provided parameters. ```typescript const ssvi = new WasmSsviSurface(rho, eta, gamma, tenors, forwards, thetas); ``` -------------------------------- ### Add volsurf Dependency Source: https://github.com/volsurf-rs/volsurf/blob/main/README.md Add the volsurf crate to your Cargo.toml file to include it in your project. ```toml [dependencies] volsurf = "2.0" ``` -------------------------------- ### Build WASM Module with wasm-pack Source: https://github.com/volsurf-rs/volsurf/blob/main/wasm/README.md Steps to build the WebAssembly module from the Rust source code using wasm-pack. ```bash rustup target add wasm32-unknown-unknown cargo install wasm-pack wasm-pack build wasm/ --target web ``` -------------------------------- ### Build a Surface from Market Data Source: https://github.com/volsurf-rs/volsurf/blob/main/README.md Construct a volatility surface using the SurfaceBuilder API by providing spot price, interest rate, and market data for different tenors. Requires at least 3 strikes per tenor for default SVI model. ```rust use volsurf::surface::{SurfaceBuilder, VolSurface}; use volsurf::{Strike, Tenor}; let strikes = vec![80.0, 90.0, 95.0, 100.0, 105.0, 110.0, 120.0]; let vols = vec![0.28, 0.24, 0.22, 0.20, 0.22, 0.24, 0.28]; let surface = SurfaceBuilder::new() .spot(100.0) .rate(0.05) .add_tenor(0.25, &strikes, &vols) .add_tenor(1.00, &strikes, &vols) .build()?; // Query vol at any (expiry, strike) point let vol = surface.black_vol(Tenor(0.5), Strike(100.0))?; ``` -------------------------------- ### WasmSabrSmile API: Construction and Calibration Source: https://github.com/volsurf-rs/volsurf/blob/main/wasm/README.md Shows how to construct a WasmSabrSmile from parameters or calibrate it from market data. ```typescript const sabr = new WasmSabrSmile(forward, expiry, alpha, beta, rho, nu); const sabr = WasmSabrSmile.calibrate(forward, expiry, beta, marketVolsFlat); ``` -------------------------------- ### SabrSmile::new and SabrSmile::calibrate Source: https://context7.com/volsurf-rs/volsurf/llms.txt Implements Hagan et al. (2002) SABR model with parameters (α, β, ρ, ν). Calibration fixes β, analytically solves α from the ATM vol, and optimizes (ρ, ν) via Nelder-Mead. Supports warm-starting from a previous calibration. ```APIDOC ## `SabrSmile::new` / `SabrSmile::calibrate` — SABR stochastic vol smile Implements Hagan et al. (2002) SABR model with parameters `(α, β, ρ, ν)` where `β` is fixed by convention (0 = normal/rates, 0.5 = equity, 1 = lognormal). The Hagan closed-form approximation maps parameters to Black implied vol for each strike. Calibration fixes `β`, analytically solves `α` from the ATM vol, and optimizes `(ρ, ν)` via Nelder-Mead in transformed space `(tanh(x), exp(y))`. Supports warm-starting from a previous calibration to speed up sequential tenor fitting. ```rust use volsurf::smile::{SabrSmile, SmileSection}; use volsurf::types::Strike; // Construct SABR smile — equity convention (beta=0.5) let smile = SabrSmile::new( 100.0, // forward 1.0, // expiry 0.3, // alpha: ATM vol scale 0.5, // beta: CIR backbone -0.3, // rho: negative equity skew 0.4, // nu: vol-of-vol (smile curvature) )?; println!("ATM vol: {:.4}%", smile.vol(Strike(100.0))?.0 * 100.0); println!("OTM call: {:.4}%", smile.vol(Strike(120.0))?.0 * 100.0); println!("OTM put: {:.4}%", smile.vol(Strike( 80.0))?.0 * 100.0); // Negative rho → put wing vol > call wing vol assert!(smile.vol(Strike(80.0))?.0 > smile.vol(Strike(120.0))?.0); // Calibrate from market data (minimum 4 points) let market: Vec<(f64, f64)> = vec![ (80.0, 0.28), (90.0, 0.24), (100.0, 0.20), (110.0, 0.22), (120.0, 0.26), ]; let calibrated = SabrSmile::calibrate(100.0, 1.0, 0.5 /* beta */, &market)?; println!("Calibrated alpha={:.4}, rho={:.4}, nu={:.4}", calibrated.alpha(), calibrated.rho(), calibrated.nu()); // Warm-start the next tenor from this calibration use volsurf::calibration::{DataFilter, WeightingScheme}; let next_tenor_market: Vec<(f64, f64)> = vec![ (80.0, 0.26), (90.0, 0.22), (100.0, 0.19), (110.0, 0.21), (120.0, 0.24), ]; let next = SabrSmile::calibrate_with_config( 100.0, 2.0, 0.5, &next_tenor_market, &DataFilter::default(), &WeightingScheme::Uniform, Some(&calibrated), // warm-start seed )?; println!("Warm-start result: alpha={:.4}", next.alpha()); # Ok::<(), volsurf::VolSurfError>(()) ``` ``` -------------------------------- ### Build WebAssembly Bindings Source: https://github.com/volsurf-rs/volsurf/blob/main/README.md Builds the WebAssembly bindings for the volsurf-rs crate using wasm-pack. This command is used for compiling Rust to WebAssembly for web applications. ```bash wasm-pack build wasm/ --target web ``` -------------------------------- ### Construct and Use EssviSurface Source: https://context7.com/volsurf-rs/volsurf/llms.txt Construct an Extended SSVI surface with maturity-dependent skew. This model captures varying smile steepness across maturities. It supports two-stage calibration: fitting per tenor and then optimizing global shape parameters. Requires specifying rho_0, rho_m, a, eta, gamma, tenors, forward prices, and theta values. ```rust use volsurf::surface::{EssviSurface, VolSurface}; use volsurf::types::{Strike, Tenor}; let tenors = vec![0.25, 0.50, 1.0, 2.0]; let forwards = vec![100.0; 4]; let thetas = vec![0.01, 0.02, 0.04, 0.08]; // Construct with maturity-dependent skew let surface = EssviSurface::new( -0.7, // rho_0: steep short-end skew -0.3, // rho_m: flatter long-end skew 0.5, // a: shape exponent 0.5, // eta 0.5, // gamma tenors.clone(), forwards.clone(), thetas.clone(), )?; // rho varies across tenors for (&t, &theta) in tenors.iter().zip(thetas.iter()) { println!("T={t:.2}: rho(theta={theta:.4})={:.4}", surface.rho(theta)); } // Two-stage calibration: fit SVI per tenor, then global shape let cal_strikes: Vec = (70..=130).step_by(5).map(|k| k as f64).collect(); let market_data: Vec> = tenors.iter() .map(|&t| cal_strikes.iter() .map(|&k| Ok((k, surface.black_vol(Tenor(t), Strike(k))?.0))) .collect::, volsurf::VolSurfError>>()) .collect::>()?; let fits = EssviSurface::fit_per_tenor(&market_data, &tenors, &forwards)?; let calibrated = EssviSurface::from_per_tenor(&fits)?; println!("rho_0={:.4}, rho_m={:.4}, a={:.4}", calibrated.rho_0(), calibrated.rho_m(), calibrated.a()); // Structural calendar-arb check (closed-form, Thm 4.1 Eq 4.10) let violations = calibrated.calendar_check_structural(); println!("Structural calendar violations: {}", violations.len()); # Ok::<(), volsurf::VolSurfError>(()) ``` -------------------------------- ### Build Volatility Surface with SurfaceBuilder Source: https://context7.com/volsurf-rs/volsurf/llms.txt Ergonomically constructs a volatility surface by accumulating per-tenor smile data and interpolating between tenors. Supports different smile models like SVI (default) and SABR. Allows injection of data filters and weighting schemes. ```rust use volsurf::surface::{SurfaceBuilder, SmileModel, VolSurface}; use volsurf::types::{Strike, Tenor}; let strikes = vec![80.0, 90.0, 95.0, 100.0, 105.0, 110.0, 120.0]; // Per-tenor smile data (SPX-like equity) let vols_3m = vec![0.30, 0.26, 0.24, 0.22, 0.23, 0.25, 0.29]; let vols_6m = vec![0.28, 0.25, 0.23, 0.21, 0.22, 0.24, 0.27]; let vols_1y = vec![0.26, 0.23, 0.22, 0.20, 0.21, 0.23, 0.26]; // Build with SVI (default) let surface = SurfaceBuilder::new() .spot(100.0) .rate(0.05) .add_tenor(0.25, &strikes, &vols_3m) .add_tenor(0.50, &strikes, &vols_6m) .add_tenor(1.00, &strikes, &vols_1y) .build()?; // Query implied vol at any (T, K) — interpolates between tenors let vol = surface.black_vol(Tenor(0.75), Strike(100.0))?; let var = surface.black_variance(Tenor(0.75), Strike(100.0))?; println!("T=0.75 K=100: vol={:.4}%, var={:.6}", vol.0 * 100.0, var.0); assert!((var.0 - vol.0 * vol.0 * 0.75).abs() < 1e-12); // vol² × T consistency // Extract a smile section at any tenor let smile = surface.smile_at(Tenor(0.75))?; let density = smile.density(Strike(100.0))?; println!("T=0.75 density={:.6}", density); // Switch to SABR backbone let sabr_surface = SurfaceBuilder::new() .spot(100.0) .rate(0.05) .model(SmileModel::Sabr { beta: 0.5 }) .add_tenor(0.25, &strikes, &vols_3m) .add_tenor(1.00, &strikes, &vols_1y) .build()?; // Arbitrage diagnostics let diag = sabr_surface.diagnostics()?; println!("Calendar violations: {}, arb-free: {}", diag.calendar_violations.len(), diag.is_free()); # Ok::<(), volsurf::VolSurfError>(()) ``` -------------------------------- ### WasmSurfaceBuilder API: Initialization and Model Selection Source: https://github.com/volsurf-rs/volsurf/blob/main/wasm/README.md Shows how to initialize a WasmSurfaceBuilder and set the underlying model (SABR, SVI, or Cubic Spline). ```typescript const builder = new WasmSurfaceBuilder(); builder.spot(100.0); builder.rate(0.05); builder.modelSabr(0.5); // or modelSvi(), modelCubicSpline() ``` -------------------------------- ### Create an SSVI Global Surface Source: https://github.com/volsurf-rs/volsurf/blob/main/README.md Instantiate an SsviSurface directly with SSVI parameters (rho, eta, gamma), tenors, forwards, and ATM variance values. This method is suitable for pre-defined SSVI surfaces. ```rust use volsurf::surface::{SsviSurface, VolSurface}; use volsurf::{Strike, Tenor}; let surface = SsviSurface::new( -0.3, 0.5, 0.5, // rho, eta, gamma vec![0.25, 0.5, 1.0], // tenors vec![100.0, 100.0, 100.0], // forwards vec![0.04, 0.08, 0.16], // thetas (ATM total variance) )?; let vol = surface.black_vol(Tenor(0.5), Strike(100.0))?; let smile = surface.smile_at(Tenor(0.5))?; ``` -------------------------------- ### WasmSsviSurface API: Query Methods Source: https://github.com/volsurf-rs/volsurf/blob/main/wasm/README.md Shows how to query black volatility and variance for a given expiry and strike. ```typescript ssvi.blackVol(expiry, strike) ssvi.blackVariance(expiry, strike) ``` -------------------------------- ### Add volsurf with Optional Features Source: https://github.com/volsurf-rs/volsurf/blob/main/README.md Include optional features like 'parallel' for rayon support or 'logging' for tracing instrumentation by specifying them in Cargo.toml. ```toml volsurf = { version = "2.0", features = ["parallel", "logging"] } ``` -------------------------------- ### WasmEssviSurface API: Construction Source: https://github.com/volsurf-rs/volsurf/blob/main/wasm/README.md Illustrates the construction of an extended SSVI surface with maturity-dependent correlation. ```typescript const essvi = new WasmEssviSurface(rho0, rhoM, a, eta, gamma, tenors, forwards, thetas); ``` -------------------------------- ### Choose a Smile Model for Surface Construction Source: https://github.com/volsurf-rs/volsurf/blob/main/README.md Specify a smile model like SABR (with beta) when building a surface. Ensure the number of strikes meets the model's requirements (e.g., 4+ for SABR). ```rust use volsurf::surface::{SurfaceBuilder, SmileModel, VolSurface}; use volsurf::{Strike, Tenor}; let surface = SurfaceBuilder::new() .spot(100.0) .rate(0.05) .model(SmileModel::Sabr { beta: 0.5 }) .add_tenor(0.25, &strikes, &vols) .add_tenor(1.00, &strikes, &vols) .build()?; ``` -------------------------------- ### Error Handling Source: https://github.com/volsurf-rs/volsurf/blob/main/wasm/README.md All constructors and query methods in volsurf-wasm throw exceptions on invalid input or calibration failures. These errors can be caught using standard try-catch blocks. ```APIDOC ## Error Handling All constructors and query methods throw on invalid input or calibration failure. Errors are returned as strings. ### Example ```typescript try { const smile = new WasmSviSmile(100, 1, 0.04, 0.4, -0.4, 0, 0.1); const vol = smile.vol(100); } catch (e) { console.error(e); // e is a string describing the error } ``` ``` -------------------------------- ### Calibrate eSSVI from Market Data Source: https://github.com/volsurf-rs/volsurf/blob/main/README.md Calibrates an eSSVI volatility surface using market data. Requires per-tenor (strike, implied_vol) pairs, tenors, and forward prices. Returns a calibrated surface or an error. ```rust use volsurf::surface::{EssviSurface, VolSurface}; use volsurf::{Strike, Tenor}; // Per-tenor (strike, implied_vol) pairs let data_3m: Vec<(f64, f64)> = (0..10) .map(|i| (80.0 + 4.0 * i as f64, 0.20 + 0.01 * (i as f64 - 5.0).abs())) .collect(); let data_1y: Vec<(f64, f64)> = (0..10) .map(|i| (80.0 + 4.0 * i as f64, 0.18 + 0.008 * (i as f64 - 5.0).abs())) .collect(); let surface = EssviSurface::calibrate( &[data_3m, data_1y], &[0.25, 1.0], // tenors &[100.0, 100.0], // forwards )?; let vol = surface.black_vol(Tenor(0.5), Strike(95.0))?; ``` -------------------------------- ### WasmPiecewiseSurface API: Query Methods Source: https://github.com/volsurf-rs/volsurf/blob/main/wasm/README.md Provides methods to query black volatility and variance from a built piecewise surface. ```typescript surface.blackVol(0.5, 100.0) surface.blackVariance(0.5, 100.0) ``` -------------------------------- ### WasmEssviSurface API: Query Methods and Properties Source: https://github.com/volsurf-rs/volsurf/blob/main/wasm/README.md Details the query methods (same as SSVI) and additional properties specific to the extended SSVI model. ```typescript // Same query methods as SSVI, plus: essvi.rho0 essvi.rhoM essvi.a essvi.thetaMax ``` -------------------------------- ### DataFilter and WeightingScheme Source: https://context7.com/volsurf-rs/volsurf/llms.txt Configuration for calibration, allowing filtering of market data by log-moneyness, minimum volatility, or vol-cliff heuristic, and controlling the weighting scheme (Vega, Uniform, ModelDefault) for market residuals. ```APIDOC ## `DataFilter` and `WeightingScheme` — Calibration configuration `DataFilter` removes illiquid or extreme strikes before calibration: clip by log-moneyness `|ln(K/F)|`, filter by minimum vol floor, or enable the vol-cliff heuristic (detects >50% consecutive vol drop). `WeightingScheme` controls how market residuals are weighted in the least-squares objective: `Vega` (weights by option vega `n(d₁)`, emphasizes the liquid ATM region), `Uniform` (equal weight), or `ModelDefault` (SVI → Vega, SABR → Uniform). ```rust use volsurf::smile::{SabrSmile, SviSmile, SmileSection}; use volsurf::calibration::{DataFilter, WeightingScheme}; use volsurf::types::Strike; // Market data including illiquid deep wings let market: Vec<(f64, f64)> = vec![ (20.0, 0.90), // deep ITM — illiquid (80.0, 0.28), (90.0, 0.24), (100.0, 0.20), (110.0, 0.22), (120.0, 0.26), (300.0, 0.80), // deep OTM — illiquid ]; // Filter to |ln(K/F)| < 0.3, ignore illiquid wings let filter = DataFilter { max_log_moneyness: Some(0.30), min_vol: Some(0.05), vol_cliff_filter: Some(true), }; // SVI with vega weighting (literature default) let svi = SviSmile::calibrate_with_config( 100.0, 1.0, &market, &filter, &WeightingScheme::Vega, )?; println!("SVI ATM vol: {:.4}%", svi.vol(Strike(100.0))?.0 * 100.0); // SABR with uniform weighting (Hagan convention) let sabr = SabrSmile::calibrate_with_config( 100.0, 1.0, 0.5, &market, &filter, &WeightingScheme::Uniform, None, // no warm-start seed )?; println!("SABR ATM vol: {:.4}%", sabr.vol(Strike(100.0))?.0 * 100.0); # Ok::<(), volsurf::VolSurfError>(()) ``` ``` -------------------------------- ### SviSmile::new and SviSmile::calibrate Source: https://context7.com/volsurf-rs/volsurf/llms.txt Constructs or calibrates a Gatheral (2004) Stochastic Volatility Inspired smile. The five parameters (a, b, ρ, m, σ) directly parameterize the total implied variance. Construction enforces no-arbitrage conditions, while calibration uses Zeliade (2009) decomposition with vega weighting. ```APIDOC ## `SviSmile::new` / `SviSmile::calibrate` — SVI parametric smile Constructs or calibrates a Gatheral (2004) Stochastic Volatility Inspired smile. The five parameters `(a, b, ρ, m, σ)` directly parameterize the total implied variance `w(k) = a + b·[ρ(k−m) + √((k−m)²+σ²)]`. Construction enforces Gatheral-Jacquier no-arbitrage conditions (Roger Lee moment bound, non-negative minimum variance). Calibration uses the quasi-explicit Zeliade (2009) decomposition with vega weighting by default. ```rust use volsurf::smile::{SviSmile, SmileSection}; use volsurf::types::Strike; // Construct from known parameters (enforces no-arb conditions) let svi = SviSmile::new( 100.0, // forward 1.0, // expiry (years) 0.04, // a: minimum variance level 0.10, // b: variance slope -0.3, // rho: skew direction 0.0, // m: moneyness shift 0.20, // sigma: smile curvature )?; // Query vol and density let vol = svi.vol(Strike(100.0))?; let den = svi.density(Strike(100.0))?; println!("ATM vol={:.4}%, density={:.6}", vol.0 * 100.0, den); // Calibrate from market (strike, implied_vol) pairs let market: Vec<(f64, f64)> = vec![ (70.0, 0.32), (80.0, 0.28), (90.0, 0.24), (95.0, 0.22), (100.0, 0.20), (105.0, 0.21), (110.0, 0.23), (120.0, 0.27), (130.0, 0.31), ]; let calibrated = SviSmile::calibrate(100.0, 1.0, &market)?; // Verify fit quality let rms: f64 = (market.iter() .map(|&(k, v)| (calibrated.vol(Strike(k))?.0 - v).powi(2)) .collect::, _>>()? .iter().sum::() / market.len() as f64).sqrt(); println!("SVI RMS vol error: {rms:.2e}"); // typically < 0.001 // Check butterfly arbitrage let report = calibrated.is_arbitrage_free()?; println!("Arb-free: {}, violations: {}", report.is_free(), report.butterfly_violations.len()); # Ok::<(), volsurf::VolSurfError>(()) ``` ``` -------------------------------- ### SsviSurface::new and SsviSurface::calibrate Source: https://context7.com/volsurf-rs/volsurf/llms.txt Constructs an SSVI (Gatheral-Jacquier 2014) volatility surface from global shape parameters and per-tenor variances, or calibrates it from market data. Supports arbitrage diagnostics. ```APIDOC ## `SsviSurface::new` / `SsviSurface::calibrate` — SSVI global surface Implements the Gatheral-Jacquier (2014) Surface SVI parameterization with three global shape parameters `(ρ, η, γ)` and per-tenor ATM total variances `θ_i`. The power-law mixing function `φ(θ) = η/θ^γ` guarantees butterfly arbitrage freedom when `η(1+|ρ|) ≤ 2`. Calendar arbitrage is checked both numerically (grid-based) and analytically. Calibration fits all tenors simultaneously by minimizing total variance residuals. ```rust use volsurf::surface::{SsviSurface, VolSurface}; use volsurf::types::{Strike, Tenor}; // Construct from known parameters let surface = SsviSurface::new( -0.3, // rho: equity skew 0.5, // eta: smile amplitude 0.5, // gamma: term structure decay vec![0.25, 0.50, 1.0, 2.0], // tenors (years) vec![100.0; 4], // forward prices vec![0.01, 0.02, 0.04, 0.08], // theta_i = sigma_ATM^2 * T )?; let vol = surface.black_vol(Tenor(0.5), Strike(100.0))?; println!("ATM 6M vol: {:.4}%", vol.0 * 100.0); // Arbitrage diagnostics (butterfly + calendar) let diag = surface.diagnostics()?; println!("Surface arb-free: {}", diag.is_free()); let cal = surface.calendar_arb_analytical(); println!("Analytical calendar violations: {}", cal.len()); // Calibrate from multi-tenor market data let tenors = vec![0.25, 0.50, 1.0, 2.0]; let forwards = vec![100.0; 4]; let strikes_grid: Vec = (70..=130).step_by(5).map(|k| k as f64).collect(); let market_data: Vec> = tenors.iter() .map(|&t| strikes_grid.iter() .map(|&k| Ok((k, surface.black_vol(Tenor(t), Strike(k))?.0))) .collect::, volsurf::VolSurfError>>()) .collect::>()?; let calibrated = SsviSurface::calibrate(&market_data, &tenors, &forwards)?; println!("Calibrated: rho={:.4}, eta={:.4}, gamma={:.4}", calibrated.rho(), calibrated.eta(), calibrated.gamma()); # Ok::<(), volsurf::VolSurfError>(()) ``` ``` -------------------------------- ### SurfaceBuilder Source: https://context7.com/volsurf-rs/volsurf/llms.txt An ergonomic builder for constructing volatility surfaces. It allows accumulating per-tenor smile data, calibrating each tenor with a chosen SmileModel (SVI, SABR, or CubicSpline), and assembling a PiecewiseSurface with cross-tenor variance interpolation. Optional data filtering and weighting schemes can be applied. ```APIDOC ## `SurfaceBuilder` — Ergonomic multi-tenor surface construction The primary entry point for surface construction from raw market data. Accumulates per-tenor `(strikes, vols)` slices, calibrates each tenor with the chosen `SmileModel` (SVI by default, or SABR/CubicSpline), and assembles a `PiecewiseSurface` with cross-tenor variance interpolation. Optional `DataFilter` and `WeightingScheme` can be injected via `filter()` and `weighting()` builder methods. ### Methods - **`new()`**: Creates a new `SurfaceBuilder` instance. - **`spot(f64)`**: Sets the spot price of the underlying asset. - **`rate(f64)`**: Sets the risk-free interest rate. - **`add_tenor(f64, &Vec, &Vec)`**: Adds a tenor (time to expiration) with corresponding strikes and vols. - **`model(SmileModel)`**: Specifies the smile model to use (e.g., `SmileModel::Svi`, `SmileModel::Sabr { beta: f64 }`, `SmileModel::CubicSpline`). Defaults to SVI. - **`filter(impl DataFilter)`**: Applies a data filter. - **`weighting(impl WeightingScheme)`**: Applies a weighting scheme. - **`build()`**: Constructs and returns the `VolSurface`. ### Returns A `Result` containing a `PiecewiseSurface` instance if successful, or a `VolSurfError` if construction fails. ### Example ```rust use volsurf::surface::{SurfaceBuilder, SmileModel, VolSurface}; use volsurf::types::{Strike, Tenor}; let strikes = vec![80.0, 90.0, 95.0, 100.0, 105.0, 110.0, 120.0]; // Per-tenor smile data (SPX-like equity) let vols_3m = vec![0.30, 0.26, 0.24, 0.22, 0.23, 0.25, 0.29]; let vols_6m = vec![0.28, 0.25, 0.23, 0.21, 0.22, 0.24, 0.27]; let vols_1y = vec![0.26, 0.23, 0.22, 0.20, 0.21, 0.23, 0.26]; // Build with SVI (default) let surface = SurfaceBuilder::new() .spot(100.0) .rate(0.05) .add_tenor(0.25, &strikes, &vols_3m) .add_tenor(0.50, &strikes, &vols_6m) .add_tenor(1.00, &strikes, &vols_1y) .build()?; // Query implied vol at any (T, K) — interpolates between tenors let vol = surface.black_vol(Tenor(0.75), Strike(100.0))?; let var = surface.black_variance(Tenor(0.75), Strike(100.0))?; println!("T=0.75 K=100: vol={:.4}%, var={:.6}", vol.0 * 100.0, var.0); assert!((var.0 - vol.0 * vol.0 * 0.75).abs() < 1e-12); // vol² × T consistency // Extract a smile section at any tenor let smile = surface.smile_at(Tenor(0.75))?; let density = smile.density(Strike(100.0))?; println!("T=0.75 density={:.6}", density); // Switch to SABR backbone let sabr_surface = SurfaceBuilder::new() .spot(100.0) .rate(0.05) .model(SmileModel::Sabr { beta: 0.5 }) .add_tenor(0.25, &strikes, &vols_3m) .add_tenor(1.00, &strikes, &vols_1y) .build()?; // Arbitrage diagnostics let diag = sabr_surface.diagnostics()?; println!("Calendar violations: {}, arb-free: {}", diag.calendar_violations.len(), diag.is_free()); # Ok::<(), volsurf::VolSurfError>(()) ``` ``` -------------------------------- ### Serialization and Deserialization with JSON Source: https://github.com/volsurf-rs/volsurf/blob/main/wasm/README.md Shows how to serialize a smile object to a JSON string and deserialize it back. ```typescript const json = smile.toJson(); const restored = WasmSviSmile.fromJson(json); ```