# What is the Vanna-Volga Hedge Surface and How Is It Calculated in Composite Metrics?

> Discover the Vanna-Volga hedge surface a 3D grid quantifying vega vanna and volga risk. Learn its calculation in composite metrics using optionstratlib.

- Repository: [Joaquin Bejar Garcia/optionstratlib](https://github.com/joaquinbejar/optionstratlib)
- Tags: deep-dive
- Published: 2026-03-04

---

**The Vanna-Volga hedge surface is a three-dimensional grid that quantifies the cost of neutralizing vega, vanna, and volga risks across different spot prices and implied volatilities, calculated in optionstratlib by combining moneyness-adjusted volatility deviations.**

The Vanna-Volga hedge surface provides derivatives traders with a spatial view of hedging costs for vanilla option portfolios. In the optionstratlib Rust library, this composite metric is implemented as a two-dimensional grid where each point represents the cost of neutralizing key volatility Greeks. Understanding how this surface is constructed enables precise risk management across varying market conditions.

## Core Concepts of the Vanna-Volga Method

The Vanna-Volga methodology addresses the limitations of simple vega hedging by accounting for how volatility sensitivity changes with both underlying price movements and volatility shifts.

### The Three Benchmark Options

The classical Vanna-Volga approach uses three benchmark options to construct a hedge that eliminates **vega**, **vanna**, and **volga** risks simultaneously:

- A 25-delta put (downside skew exposure)
- An ATM option (pure volatility exposure)
- A 25-delta call (upside skew exposure)

### Hedge Cost Formula

In [`src/metrics/composite/vanna_volga.rs`](https://github.com/joaquinbejar/optionstratlib/blob/main/src/metrics/composite/vanna_volga.rs), the library employs a simplified computational model to determine hedge costs across the grid:

```text
Cost = VannaComponent + VolgaComponent

VannaComponent = moneyness × |σ – σ_ATM| × 100
VolgaComponent = (|σ – σ_ATM|)² × 50

```

Where:

- `σ` represents the volatility at the specific grid point
- `σ_ATM` denotes the ATM volatility extracted from the option chain
- `moneyness` calculates as `|S – S_ATM| / S_ATM` (relative distance from ATM spot)

### Grid Construction Parameters

The surface generation requires defining a regular grid through four parameters:

1. `price_range` – Tuple containing lower and upper spot price bounds
2. `vol_range` – Tuple containing lower and upper volatility bounds (as decimals)
3. `price_steps` – Number of discrete intervals along the price axis
4. `vol_steps` – Number of discrete intervals along the volatility axis

### Surface Data Structure

The implementation returns a `Result<Surface, SurfaceError>` where `Surface` maintains a `BTreeSet<Point3D>`. Each `Point3D` stores the coordinate triple `(price, vol, cost)` as `x`, `y`, and `z` values respectively, ensuring uniqueness and automatic ordering.

## Implementation in optionstratlib

The Vanna-Volga hedge surface functionality spans multiple modules, with clear separation between the trait interface and concrete chain implementations.

### Trait Definition

The `VannaVolgaSurface` trait in [`src/metrics/composite/vanna_volga.rs`](https://github.com/joaquinbejar/optionstratlib/blob/main/src/metrics/composite/vanna_volga.rs) (lines 90-119) defines the contract for generating hedge surfaces:

```rust
pub trait VannaVolgaSurface {
    fn vanna_volga_surface(
        &self,
        price_range: (Positive, Positive),
        vol_range: (Positive, Positive),
        price_steps: usize,
        vol_steps: usize,
    ) -> Result<Surface, SurfaceError>;
}

```

This abstraction allows different data structures to implement the Vanna-Volga calculation logic while maintaining a consistent interface.

### Concrete Implementation for OptionChain

The primary implementation resides in [`src/chains/chain.rs`](https://github.com/joaquinbejar/optionstratlib/blob/main/src/chains/chain.rs) (lines 3811-3895) within the `OptionChain` struct. This implementation:

1. Extracts the ATM volatility from available options
2. Constructs the price-volatility grid
3. Computes Vanna and Volga cost components for each point
4. Aggregates results into a `Surface` structure

## Step-by-Step Calculation Algorithm

The algorithm implemented in [`src/chains/chain.rs`](https://github.com/joaquinbejar/optionstratlib/blob/main/src/chains/chain.rs) follows a precise sequence to generate the hedge surface:

**Step 1: ATM Volatility Identification**

The system locates the option with strike price closest to the underlying spot where implied volatility is non-zero. If no valid option exists, it defaults to **0.20** (20%) as the ATM volatility reference:

```rust
let atm_vol = self.options.iter()
    .filter(|opt| !opt.implied_volatility.is_zero())
    .min_by(|a, b| {
        let diff_a = (a.strike_price.to_dec() - self.underlying_price.to_dec()).abs();
        let diff_b = (b.strike_price.to_dec() - self.underlying_price.to_dec()).abs();
        diff_a.partial_cmp(&diff_b).unwrap_or(Ordering::Equal)
    })
    .map(|opt| opt.implied_volatility.to_dec())
    .unwrap_or(dec!(0.20));

```

**Step 2: Grid Step Calculation**

The price and volatility steps are computed as the interval size divided by the number of steps, with protection against zero-step division.

**Step 3: Point-wise Cost Computation**

For each grid coordinate `(price, vol)`:

- Compute **moneyness**: `(price - S_ATM).abs() / S_ATM`
- Compute **volatility difference**: `|vol - σ_ATM|`
- Compute **Vanna cost**: `moneyness * vol_diff * 100`
- Compute **Volga cost**: `vol_diff * vol_diff * 50`
- Sum to obtain **Vanna-Volga cost**

```rust
let moneyness = (price - self.underlying_price.to_dec()).abs()
    / self.underlying_price.to_dec();
let vol_diff = (vol - atm_vol).abs();

let vanna_cost = moneyness * vol_diff * dec!(100.0);
let volga_cost = vol_diff * vol_diff * dec!(50.0);
let vv_cost = vanna_cost + volga_cost;

```

**Step 4: Surface Construction**

Each calculated point is inserted into a `BTreeSet<Point3D>` to guarantee uniqueness and ordering. The final `Surface` is returned if points exist; otherwise, a `SurfaceError::ConstructionError` is emitted.

## Practical Code Examples

### Generating a Surface from an Option Chain

The following example demonstrates loading an option chain and computing the Vanna-Volga hedge surface:

```rust
use optionstratlib::chains::OptionChain;
use positive::pos_or_panic;
use rust_decimal_macros::dec;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Load an option chain from a JSON file (the file must follow the library schema)
    let chain = OptionChain::load_from_json("data/options.json")?;

    // Define the grid
    let price_range = (pos_or_panic!(400.0), pos_or_panic!(500.0));
    let vol_range   = (pos_or_panic!(0.10), pos_or_panic!(0.40));

    // Build a 20×20 surface
    let surface = chain.vanna_volga_surface(price_range, vol_range, 20, 20)?;

    // Iterate over a few points (price, vol, hedge‑cost)
    for point in surface.points.iter().take(5) {
        println!(
            "S={:.2}, σ={:.2%}, cost={:.4}",
            point.x,
            point.y,
            point.z
        );
    }

    Ok(())
}

```

### Custom Trait Implementation

For testing or alternative pricing models, you can implement the `VannaVolgaSurface` trait directly:

```rust
use optionstratlib::metrics::VannaVolgaSurface;
use positive::pos_or_panic;
use rust_decimal_macros::dec;

struct MySurface;

impl VannaVolgaSurface for MySurface {
    fn vanna_volga_surface(
        &self,
        price_range: (Positive, Positive),
        vol_range: (Positive, Positive),
        price_steps: usize,
        vol_steps: usize,
    ) -> Result<Surface, SurfaceError> {
        // ... (same logic as OptionChain) ...
        unimplemented!()
    }
}

fn demo() {
    let surf = MySurface;
    let price_range = (pos_or_panic!(380.0), pos_or_panic!(520.0));
    let vol_range   = (pos_or_panic!(0.05), pos_or_panic!(0.45));

    let surface = surf.vanna_volga_surface(price_range, vol_range, 10, 10).unwrap();
    println!("Generated {} points", surface.points.len());
}

```

This mirrors the trait contract defined in **src/metrics/composite/vanna_volga.rs** and can be used for unit-testing or alternative pricing models.

## Key Source Files

The Vanna-Volga hedge surface implementation spans several modules within the optionstratlib repository:

| File (relative to repository root) | What It Contains |
|-----------------------------------|------------------|
| [`src/metrics/composite/vanna_volga.rs`](https://github.com/joaquinbejar/optionstratlib/blob/main/src/metrics/composite/vanna_volga.rs) | Definition of the `VannaVolgaSurface` trait, documentation, and tests that illustrate the expected behaviour. |
| [`src/chains/chain.rs`](https://github.com/joaquinbejar/optionstratlib/blob/main/src/chains/chain.rs) (around line 3811) | Concrete implementation of the trait for `OptionChain`, including the grid logic, ATM‑vol extraction, and cost calculation. |
| [`src/surfaces/mod.rs`](https://github.com/joaquinbejar/optionstratlib/blob/main/src/surfaces/mod.rs) | Types `Surface` and `Point3D` used to store the generated 3‑D points. |
| [`src/greeks/equations.rs`](https://github.com/joaquinbejar/optionstratlib/blob/main/src/greeks/equations.rs) | Calculation of the underlying Greeks (including `vanna`) that feed into the composite metric. |
| [`tests/unit/chain/composite_metrics_test.rs`](https://github.com/joaquinbejar/optionstratlib/blob/main/tests/unit/chain/composite_metrics_test.rs) | Integration tests that validate the Vanna‑Volga surface behaviour against known expectations. |

These files collectively implement the Vanna‑Volga hedge surface, expose it through a clean trait, and provide the infrastructure for visualising and analysing volatility‑smile‑aware hedging costs.

## Summary

- The **Vanna-Volga hedge surface** maps hedging costs across price and volatility dimensions using a simplified decomposition into vanna and volga components.
- **optionstratlib** implements this in [`src/chains/chain.rs`](https://github.com/joaquinbejar/optionstratlib/blob/main/src/chains/chain.rs) by extracting ATM volatility, iterating over a defined grid, and computing costs using moneyness-adjusted volatility deviations.
- The **VannaVolgaSurface** trait in [`src/metrics/composite/vanna_volga.rs`](https://github.com/joaquinbejar/optionstratlib/blob/main/src/metrics/composite/vanna_volga.rs) provides a reusable interface for any structure requiring volatility-smile-aware hedging analysis.
- Each surface point represents the cumulative cost of neutralizing vega, vanna, and volga risks at specific price-volatility coordinates.

## Frequently Asked Questions

### How does the Vanna-Volga hedge surface differ from standard vega hedging?

Standard vega hedging only neutralizes sensitivity to parallel shifts in implied volatility. The **Vanna-Volga hedge surface** additionally accounts for **vanna** (sensitivity of delta to volatility changes) and **volga** (sensitivity of vega to volatility changes), providing protection against volatility smile movements and skew shifts.

### What are the default values used when ATM volatility cannot be determined?

If the implementation in [`src/chains/chain.rs`](https://github.com/joaquinbejar/optionstratlib/blob/main/src/chains/chain.rs) cannot locate a valid ATM option with non-zero implied volatility, it defaults to **0.20** (20%) as the ATM volatility reference. This fallback ensures surface generation continues even with incomplete chain data.

### Can the Vanna-Volga surface be used for exotic options or only vanilla options?

The current implementation in optionstratlib is optimized for **vanilla options** through the `OptionChain` structure. While the `VannaVolgaSurface` trait can theoretically be implemented for exotic instruments, the underlying hedge-cost formula assumes standard moneyness calculations based on strike-to-spot relationships typical of vanilla contracts.

### How does the grid resolution affect calculation performance?

The algorithm performs nested iteration over `price_steps × vol_steps` grid points. Increasing resolution improves surface smoothness but scales computation **quadratically**. For production use, the implementation in [`src/chains/chain.rs`](https://github.com/joaquinbejar/optionstratlib/blob/main/src/chains/chain.rs) recommends balancing granularity with performance requirements, typically using 20×20 or 50×50 grids for real-time applications.