Introduction.
I derived the Black-Scholes formula for European style vanilla FX options in a previous post here. The Black-Scholes model \(Bl(S_0,K,T,r_{DOM},r_{FOR},\sigma)\) equipped with a single flat volatility parameter \(\sigma\) produces option prices which are NOT consistent with the observed market prices of FX options across different strikes and maturities.
Although, the BS model suffers many flaws, it is still often used, at least for quoting purposes. Since all of the other inputs into the model - market data variables such as the stock price \(S_0\), the domestic depo rate \(r_{DOM}\), the foreign depo rate \(r_{FOR}\), and the parameters such as option strike \(K\), the time-to-maturity \(T\), can be either seen in the market or are known constants, we can easily solve for the value \(\sigma_{\text{imp}}\) of the parameter \(\sigma\) such that:
\[Bl(S_0,K,T,r_{DOM},r_{FOR},\sigma_{\text{imp}}) = V_{\text{market}}\]
This value \(\sigma_{\text{imp}}\) implied from the market price of the option is called the implied volatility.
Thus, although the BS model suffers from flaws, it is mainly used as a quote converter. In the FX options market, option prices are quoted in terms of implied volatilities. The BS formula is used to convert implied vols \(\sigma_{\text{imp}}\) to prices and vice versa. The delta hedge to be exchanged between counterparties is calculated according to the BS formula, and this is also true for the Vega hedge of various exotic options. In many cases, the model is also used to run trading books.
In this note, I explore various delta conventions and derive the greeks. Check out FX Vol smile by Wyestup! The entire concept of the FX volatility smile is based on the parametrization with respect to delta.
Quote style conversions.
In FX markets, options are quoted in one of 4 quote styles - domestic per foreign (d/f), percentage foreign (%f), percentage domestic (%d) and foreign per domestic (f/d).
The standard Black-Scholes formula is:
\[ \begin{align*} V_{d/f} &= \omega [S_0 e^{-r_{FOR} T} \Phi(d_{+}) - K e^{-r_{DOM}T} \Phi(d_{-})\\ &= \omega e^{-r_{DOM}T}[F \Phi(d_{+}) - K \Phi(d_{-})] \end{align*} \]
Implementing the Bl Calculator and Option Greeks.
import numpy as np
from scipy.stats import norm
from enum import Enum
import datetime as dt
class CallPut(Enum):
= 1
CALL_OPTION = -1
PUT_OPTION
class BlackCalculator:
"""Implements the Black formula to price a vanilla option"""
def __init__(
self,
float,
s_t : float,
strike : float,
today : float,
expiry : float,
r_dom : float,
r_for : float
sigma :
)self._s_t = s_t
self._strike = strike
self._today = today
self._expiry = expiry
self._r_dom = r_dom
self._r_for = r_for
self._sigma = sigma
def at_the_money_forward(
self,
-> float :
) """Computes the at-the-money forward"""
= np.exp(self._r_for * (expiry - today))
foreign_df = np.exp(self._r_dom * (expiry - today))
domestic_df = foreign_df / domestic_df
fwd_points return self._s_t * fwd_points
def d_plus(S_t,K,t,T,r_DOM,r_FOR,sigma):
= at_the_money_forward(S_t,K,t,T,r_DOM,r_FOR,sigma)
F return (np.log(F/K) + (T-t)*(sigma**2)/2)/(sigma * np.sqrt(T - t))
def d_minus(S_t,K,t,T,r_DOM,r_FOR,sigma):
= at_the_money_forward(S_t,K,t,T,r_DOM,r_FOR,sigma)
F return (np.log(F/K) - (T-t)*(sigma**2)/2)/(sigma * np.sqrt(T - t))
def pv(S_t,K,t,T,r_DOM,r_FOR,sigma, CCY1Notional,callPut):
= at_the_money_forward(S_t,K,t,T,r_DOM,r_FOR,sigma)
F = callPut.value
omega = dPlus(S_t,K,t,T,r_DOM,r_FOR,sigma)
d_plus = dMinus(S_t,K,t,T,r_DOM,r_FOR,sigma)
d_minus = np.exp(-r_DOM*(T-t))
domesticDF
= omega* (F * norm.cdf(omega * d_plus) - K * norm.cdf(omega * d_minus))
undiscountedPrice = domesticDF * undiscountedPrice * CCY1Notional
pv return pv