σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft AcademySkills
Ctrl K
AO
+1.2k Learners
LoginJoin ↗
Skills
AO
+1.2k Learners
Login
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python skill›Module 8 · Stochastic models & machine learning›Topic 8.3·~55 minAdvanced

Itô's lemma

Skill positionModule 8 of 11 · Topic 3 of 10
8.3 Itô's lemma
—Lab score— / 100Total XP—Streak—

A chain rule with a catch

If S were a smooth function of time, the change of f(S) would be f'(S) * dS, the ordinary chain rule. But dS contains dW, and (dW)^2 = dt is not negligible: it is of the same order as dt. Expanding f(S + dS) to second order, the term 0.5 * f''(S) * (dS)^2 survives, and (dS)^2 = sigma^2 * S^2 * dt. That extra term is Itô's lemma, the main tool for deriving option prices.

def ito_drift(s, mu, sigma, f_t, f_s, f_ss):
    return f_t + mu * s * f_s + 0.5 * sigma ** 2 * s ** 2 * f_ss

# f(S) = S: the drift is just mu * S (no curvature, no extra term)
print(ito_drift(100.0, 0.08, 0.2, 0.0, 1.0, 0.0))             # 8.0
# f(S) = S squared: f_S = 2 S and f_SS = 2, so the curvature term appears
print(ito_drift(100.0, 0.08, 0.2, 0.0, 2 * 100.0, 2.0))        # 1600 + 400 = 2000.0
# The drift for several functions of the price, S = 100, mu = 8%, sigma = 20%
def ito_drift(s, mu, sigma, f_t, f_s, f_ss):
    return f_t + mu * s * f_s + 0.5 * sigma ** 2 * s ** 2 * f_ss

s, mu, sigma = 100.0, 0.08, 0.2
print("S      ", ito_drift(s, mu, sigma, 0.0, 1.0, 0.0))                  # 8.0
print("S^2    ", ito_drift(s, mu, sigma, 0.0, 2 * s, 2.0))                # 2000.0
print("S^3    ", ito_drift(s, mu, sigma, 0.0, 3 * s ** 2, 6 * s))         # 240000 + 120000 = 360000.0
print("sqrt(S)", round(ito_drift(s, mu, sigma, 0.0, 0.5 / s ** 0.5, -0.25 / s ** 1.5), 4))    # 0.35 : curvature pulls it down

The log price and the sigma squared over two

Apply the lemma to f = ln S. Here f_t = 0, f_S = 1/S and f_SS = -1/S^2, so the drift is mu - 0.5 * sigma^2 and the diffusion sigma: d ln S = (mu - sigma^2/2) dt + sigma dW. The log price is an arithmetic Brownian motion, which is exactly why the solution of GBM has mu - sigma^2/2 in the exponent. The convexity of exp and the concavity of ln are two sides of the same fact.

def ito_drift(s, mu, sigma, f_t, f_s, f_ss):
    return f_t + mu * s * f_s + 0.5 * sigma ** 2 * s ** 2 * f_ss

def log_drift(mu, sigma):
    s = 1.0                                        # the answer does not depend on s
    return ito_drift(s, mu, sigma, 0.0, 1.0 / s, -1.0 / s ** 2)

print(log_drift(0.08, 0.20))                       # 0.06   = 0.08 - 0.5 * 0.2**2
print(round(log_drift(0.08, 0.60), 4))             # -0.1   a high volatility makes the typical growth negative

Checking the lemma by simulation

You do not have to take the lemma on trust. Take many random one-step moves s_new = s + mu * s * dt + sigma * s * sqrt(dt) * z, compute f(s_new) - f(s) for a function f, average and divide by dt. The result converges to the drift from the lemma. With f = S^2 and the curvature term left out, the simulation visibly disagrees, which is the best way to see that the extra term is real.

import numpy as np

def mean_change_estimate(f, s, mu, sigma, dt, z):
    s_new = s + mu * s * dt + sigma * s * np.sqrt(dt) * z
    return float(np.mean(f(s_new) - f(s)) / dt)

half = np.random.default_rng(1).standard_normal(200_000)
z = np.concatenate([half, -half])                 # pairs of opposite draws: the noise at first order cancels
estimate = mean_change_estimate(lambda x: x ** 2, 100.0, 0.08, 0.2, 0.001, z)
print(round(estimate))                                       # about 2000
print(round(0.08 * 100.0 * 2 * 100.0))                       # 1600  the ordinary chain rule alone: the curvature term (400) is missing
Why it matters for optionsTo price an option you apply the lemma to its value V(S, t). The drift contains V_t, mu * S * V_S and 0.5 * sigma^2 * S^2 * V_SS; delta-hedging removes the dW term, and the result is the Black–Scholes equation, which has the Greeks of the earlier topics in it: theta, delta and gamma. The risk-free rate replaces mu, and the stock's expected return drops out of the price.
  • Ito's lemma: the drift of f(S, t) is f_t + mu * S * f_S + 0.5 * sigma^2 * S^2 * f_SS and the noise coefficient sigma * S * f_S.
  • The curvature term exists because (dW)^2 = dt; for ln S it gives the drift mu - sigma^2 / 2 of the GBM solution.
  • Verify by simulation: the average of f(s_new) - f(s) over dt matches the lemma; applied to an option value it yields Black–Scholes.

Next up: Asian options, whose payoff depends on the average price over the life of the option.

Lab · what's graded

  • Find the drift of a function with ito_drift25 pts
  • Find the noise coefficient with ito_diffusion25 pts
  • Apply it to the log price with log_drift25 pts
  • Check it by simulation with mean_change_estimate25 pts
Explorer · lab-104-1
▾ lab-104-1
…
bash · in-browser
learner@quantcraft:~/lab-104-1$
Python not loaded yetPythonLn 1, Col 1Spaces: 4UTF-8

Reset this topic?

Everything in lab-104-1 goes back to the starter files and the terminal is cleared. Your current solution will be lost — this can't be undone.

Will be reset
  • main.py → starter code
  • Terminal history and test output
  • Unsubmitted changes
Will be kept
  • Best lab score
  • Your XP and streak
  • Lecture progress and notes

Log in to run your code.

Every lecture is free to read. Create an account or log in to run code, run tests and submit this lab.

  • In-browser IDE with a bash terminal — nothing to install
  • Instant grading, XP and your place on the skill
Create an accountLog in
OR
Continue with GitHub