### 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);
```