#quant #arbitrage #calibration #volatility #options

sanos

SANOS: Smooth strictly Arbitrage-free Non-parametric Option Surfaces (Rust implementation)

2 releases

new 0.1.1 Feb 17, 2026
0.1.0 Feb 17, 2026

#268 in Math

MIT license

210KB
5.5K SLoC

sanos

Rust implementation of SANOS: smooth, arbitrage-aware option surface calibration.

This crate provides:

  • market data structures (OptionBook, OptionChain, CallQuote)
  • calibration pipeline (calibrate, calibrate_with_stats)
  • resulting surface object (SanosSurface)

API docs: https://docs.rs/sanos

Installation

[dependencies]
sanos = "0.1"

Input Conventions

  • Quotes are forward-normalized call prices in [0, 1].
  • Strikes are forward moneyness (k > 0).
  • Maturities must be strictly increasing in OptionBook.
  • Use at least 2 maturities if you want to query interpolated surface prices (surface.call).

Quick Start

use sanos::calibration::{calibrate, CalibrationConfig};
use sanos::error::SanosResult;
use sanos::market::OptionBook;

fn run(book: &OptionBook, cfg: &CalibrationConfig) -> SanosResult<f64> {
    let surface = calibrate(book, cfg)?;
    surface.call(1.0, 1.0)
}

End-to-End Example (OptionBook + Config)

use sanos::backbone::{bs_call_forward_norm, BackboneConfig, BsTimeChangedConfig};
use sanos::calibration::{calibrate, CalibrationConfig, ConvexOrderValidationMode};
use sanos::error::SanosResult;
use sanos::fit::FitConfig;
use sanos::grid::StrikeGridPolicyConfig;
use sanos::interp::TimeInterpConfig;
use sanos::market::{CallQuote, OptionBook, OptionChain};

fn build_synthetic_book() -> SanosResult<OptionBook> {
    // Two maturities with synthetic ATM total variances.
    let maturities = [0.5, 1.0];
    let total_vars = [0.04, 0.09];
    let strikes = [0.8, 0.9, 1.0, 1.1, 1.2];

    let mut chains = Vec::new();
    for (t, w) in maturities.into_iter().zip(total_vars) {
        let mut quotes = Vec::new();
        for k in strikes {
            let mid = bs_call_forward_norm(k, w)?;
            let spread = 0.01;
            let bid = (mid - 0.5 * spread).clamp(0.0, 1.0);
            let ask = (mid + 0.5 * spread).clamp(0.0, 1.0);
            quotes.push(CallQuote::new(k, bid, ask, 1.0)?);
        }
        chains.push(OptionChain::new(t, quotes)?);
    }

    OptionBook::new(chains)
}

fn build_config() -> CalibrationConfig {
    CalibrationConfig {
        backbone: BackboneConfig::BsTimeChanged(BsTimeChangedConfig::default()),
        grid: StrikeGridPolicyConfig::default(),
        fit: FitConfig::default(),
        time_interp: TimeInterpConfig::default(),
        convex_order_validation: ConvexOrderValidationMode::Error,
    }
}

fn main() -> SanosResult<()> {
    let book = build_synthetic_book()?;
    let cfg = build_config();

    let surface = calibrate(&book, &cfg)?;
    let c = surface.call(0.75, 1.0)?;
    println!("Interpolated call(T=0.75, K=1.0) = {c:.6}");
    Ok(())
}

Config Example

use sanos::backbone::{BackboneConfig, BsTimeChangedConfig};
use sanos::calibration::{CalibrationConfig, ConvexOrderValidationMode};
use sanos::fit::FitConfig;
use sanos::grid::StrikeGridPolicyConfig;
use sanos::interp::TimeInterpConfig;

let cfg = CalibrationConfig {
    backbone: BackboneConfig::BsTimeChanged(BsTimeChangedConfig::default()),
    grid: StrikeGridPolicyConfig::default(),
    fit: FitConfig::default(), // default solver = Microlp
    time_interp: TimeInterpConfig::default(),
    convex_order_validation: ConvexOrderValidationMode::Error,
};

Feature Flags

  • lp-microlp (default): pure-Rust LP solver backend.
  • lp-cbc: CBC backend via good_lp/lp-solvers (requires CBC runtime).
  • iv-jaeckel (default): implied-vol inversion support.
  • serde: serialization support for config/runtime types.

When selecting a solver in configuration, the matching crate feature must be enabled.

Scope

This crate is self-contained and usable as-is for building option books, calibrating SANOS surfaces, and querying interpolated prices.

Roadmap

Additional tooling and integrations may be published later, after further validation.

Research Attribution

This crate is an independent implementation of the SANOS methodology described in:

The code in this repository is original Rust code released under MIT (LICENSE), and is not a copy of the paper text.

Non-Affiliation

This project is not affiliated with, endorsed by, or maintained by the authors of the SANOS paper.

Pre-publish Checklist

cargo test -p sanos
cargo test -p sanos --no-default-features
RUSTDOCFLAGS="-D warnings" cargo doc -p sanos --no-deps
cargo package -p sanos

Dependencies

~6–12MB
~261K SLoC