C++ for Quants
  • Home
Category:

Volatility

Implied Volatility
Volatility

Compute the Implied Volatility for a Call Option in C++

by Clement Daubrenet June 21, 2025

Volatility is a cornerstone concept in finance, used to measure the degree of variation in the price of a financial instrument. Two of the most commonly referenced types are Implied Volatility (IV) and Historical Volatility (HV), and while they both aim to assess risk, they do so from different perspectives.

Implied Volatility is forward-looking. Derived from option prices using models like Black-Scholes, it reflects the market’s expectations of future price movement. Higher IV typically signals greater anticipated risk or uncertainty.

HV is objective and purely data-driven, while IV incorporates market sentiment and supply-demand dynamics in the options market.

1. Derive Black-Scholes PDE Solution for European Call Options

To derive implied volatility from the Black-Scholes model, you’re essentially trying to find the volatility (σ) that, when plugged into the Black-Scholes formula, yields the market price of the option.

Let’s start from Black-Scholes.

[math]
\Large{dS_t = \mu S_t \, dt + \sigma S_t \, dW_t}
[/math]

is a stochastic differential equation (SDE) — it’s the core model behind how asset prices evolve over time in the Black-Scholes framework.


🔍 Term-by-Term Breakdown

SymbolMeaning
[math] S_t​ [/math]The price of the asset (e.g., stock) at time ttt
[math] dS_t [/math]The infinitesimal change in the asset price over a small time interval
[math] \mu [/math] The drift — average rate of return (expected % growth per unit time)
dtA very small time interval (infinitesimal)
[math] \sigma [/math]The volatility — standard deviation of returns per unit time (how “noisy” the price is)
[math] dWt [/math]A random shock, also called a Wiener process or Brownian motion increment. Represents randomness.

The formula models price changes as having two components:

A deterministic part:

[math] \Large{\mu S_t \, dt} [/math]

This is the trend — the predictable, average growth over time.

A stochastic part:

[math] \Large{\sigma S_t \, dW_t} [/math]

This is the randomness — the noise or uncertainty in price movements.

  • [math] \sigma [/math] scales the size of the randomness.
  • [math] dW_t​ [/math] brings in randomness from a standard Brownian motion: the mean is 0 and the variance is dt

A first thing to do to reach the implied volatility question is to notice that to price options, we don’t use [math] mu [/math] because investors care about risk-adjusted returns.

Instead, we use r for the risk-neutral measure [math] \mathbb{Q} [/math], where:

[math] \Large{dS_t = r S_t \, dt + \sigma S_t \, dW_t^{\mathbb{Q}}} [/math]

  • The drift becomes r, the risk-free rate.
  • The randomness remains via [math] \sigma [/math] and [math] dW_t [/math]

🔍 Let’s define the call option price


[math]\Large{ C(t,S_t​)=price of a call option} [/math]

After a bit of work that we will not do in this article, we can derive the Black-Scholes PDE:

[math] \Large{\frac{\partial C}{\partial t} + r S_t \frac{\partial C}{\partial S} + \frac{1}{2} \sigma^2 S_t^2 \frac{\partial^2 C}{\partial S^2} – r C = 0} [/math]

Solving this PDE with the terminal condition:

[math] \Large{C(T, S_T) = \max(S_T – K, 0)} [/math]

gives us the option price at time t=0:

[math] \Large{C(\sigma) = S_0 \Phi(d_1) – K e^{-rT} \Phi(d_2)} [/math]

where:

[math] \Large{d_1 = \frac{\ln(S_0 / K) + (r + \frac{1}{2} \sigma^2) T}{\sigma \sqrt{T}}} [/math]

And

[math] \Large{d_2 = d_1 – \sigma \sqrt{T}} [/math]

2. Determine the Equation to Solve to get the Implied Volatility

We now take the market-observed option price [math] C_{market} [/math] and ask:

“What value of [math] \sigma [/math] makes the Black-Scholes price equal the market price?”

So we define the equation to solve:

[math] \Large{f(\sigma) = C(\sigma) \, – C_{\text{market}} = 0} [/math]

Since C(σ)C(\sigma)C(σ) involves Φ(d1)\Phi(d_1)Φ(d1​) and d1d_1d1​ contains σ\sigmaσ both in the numerator and denominator, there is no closed-form algebraic solution.

We solve it using numerical methods:

  • Brent’s method (robust)
  • Newton-Raphson (faster, requires Vega)
  • Bisection (simple, slower)

What does it mean to solve that equation in a real-world context?

🧮 Example

Let’s say the following is true today:

ParameterValue
[math] S_0 [/math]​ (stock price)$100
K (strike)$105
T (time to maturity)30 days ≈ 0.0822 years
r (risk-free rate)5% = 0.05
[math] C_{market} [/math] option price from market$2.50

👇 Now We Need To Solve:

[math] \Large{f(\sigma) = C(\sigma) – 2.50 = 0} [/math]

You plug different values of [math] \sigma [/math] into the Black-Scholes formula:

[math] \Large{C(\sigma) = S_0 \Phi(d_1) – K e^{-rT} \Phi(d_2)} [/math]

where:

[math] \Large{d_1 = \frac{\ln(S_0 / K) + (r + \frac{1}{2} \sigma^2) T}{\sigma \sqrt{T}}} [/math]

[math] \Large{d_2 = d_1 – \sigma \sqrt{T}} [/math]

And iterate until:

[math] \Large{C(\sigma) \approx 2.50} [/math].

So, how do we iterate to solve the volatility equation?

3. Use Quantlib to Solve the Implied Volatility Equation

Quantlib will solve that equation with a numerical method.

Here is the code for the example above:

#include <ql/quantlib.hpp>
#include <iostream>

using namespace QuantLib;

int main() {
    Calendar calendar = TARGET();
    Date today = Date::todaysDate();
    Settings::instance().evaluationDate() = today;

    // Option parameters
    Option::Type optionType = Option::Call;
    Real underlying = 100.0;
    Real strike = 105.0;
    Rate riskFreeRate = 0.05;
    Volatility initialVol = 0.20; // Just an initial guess
    Date maturity = calendar.advance(today, Period(30, Days));  // 30-day maturity
    Real marketPrice = 2.50;

    DayCounter dayCounter = Actual365Fixed();
    Handle<Quote> underlyingH(boost::shared_ptr<Quote>(new SimpleQuote(underlying)));

    // Constructing the yield curve and flat volatility surface
    Handle<YieldTermStructure> flatTermStructure(
        boost::shared_ptr<YieldTermStructure>(
            new FlatForward(today, riskFreeRate, dayCounter)));

    Handle<BlackVolTermStructure> flatVolTS(
        boost::shared_ptr<BlackVolTermStructure>(
            new BlackConstantVol(today, calendar, initialVol, dayCounter)));

    boost::shared_ptr<StrikedTypePayoff> payoff(
        new PlainVanillaPayoff(optionType, strike));

    boost::shared_ptr<Exercise> exercise(
        new EuropeanExercise(maturity));

    EuropeanOption option(payoff, exercise);

    Handle<YieldTermStructure> flatDividendTS(
    boost::shared_ptr<YieldTermStructure>(
        new FlatForward(today, 0.0, dayCounter)));

    boost::shared_ptr<BlackScholesMertonProcess> bsmProcess(
        new BlackScholesMertonProcess(underlyingH, 
                                      flatDividendTS, 
                                      flatTermStructure, 
                                      flatVolTS));

    // Calculate implied volatility
    try {
        Volatility impliedVol = option.impliedVolatility(
            marketPrice, bsmProcess, 1e-6, 100, 0.0001, 4.0);
        std::cout << "Implied Volatility: " << impliedVol << std::endl;
    } catch (std::exception& e) {
        std::cerr << "Error calculating implied volatility: " << e.what() << std::endl;
    }

    return 0;
}

after:

brew install quantlib

and setting up your CMakeLists.txt:

cmake_minimum_required(VERSION 3.10)
project(QuantLibImpliedVolExample)

set(CMAKE_CXX_STANDARD 17)

find_package(PkgConfig REQUIRED)
pkg_check_modules(QUANTLIB REQUIRED QuantLib)

include_directories(${QUANTLIB_INCLUDE_DIRS})
link_directories(${QUANTLIB_LIBRARY_DIRS})

add_executable(implied_vol src/volatility.cpp)
target_link_libraries(implied_vol ${QUANTLIB_LIBRARIES})

and now you can compile:

mkdir build
cd build
cmake ..
make

Eventually, you can run the executable:

> ./implied_vol
Implied Volatility: 0.318886
June 21, 2025 0 comments

@2025 - All Right Reserved.


Back To Top
  • Home