Skip to content
All library documents

Pricing American FX Options with QuantLib Processes and Lattice Engines

Article Quant Q&A · Author: bouwerp

Summary

The document addresses pricing American-style foreign exchange options in QuantLib when both domestic and foreign interest rates matter. The accepted answer explains that a Garman–Kohlhagen process, which incorporates both currency yield curves, can be supplied to pricing engines that accept a generalized Black–Scholes process. This resolves the question of how to represent the two rates in the process setup.

For American exercise, the answer recommends considering a binomial tree engine or a finite-difference engine instead of Monte Carlo with Longstaff–Schwartz, which may add unnecessary computational cost for this task. It also cautions that QuantLib’s VanillaOption models spot options, leaving a limitation for pricing an option whose underlying is a forward. The example code and stated inputs do not establish that historical volatility is an appropriate forecast, and the answer does not resolve the question about how the underlying forward’s maturity relates to early exercise.

Key ideas

  • A Garman–Kohlhagen process represents both domestic and foreign rate curves for FX options.
  • American option engines that accept a generalized Black–Scholes process can use that process.
  • Binomial trees and finite-difference methods are suggested alternatives to Longstaff–Schwartz Monte Carlo.
  • QuantLib’s VanillaOption represents spot options, which limits its direct fit for forward options.
  • The answer does not assess the suitability of historical volatility or fully explain forward maturity under early exercise.

Tags

Full text
# Pricing an American FX Option using Quantlib


# Pricing an American FX Option using Quantlib












I need some guidance on valuing American style FX options (spots and forwards) using quantlib in Python. Given the following parameters:

- Domestic and foreign risk-free rates

- Current market spot and forward points for the underlying asset (maturity at option exercise date plus tenor) - as an aside, does the maturity move forward for an early exercise of the option?

- Approximate volatility of the underlying (by using historical prices)

- Strike price

- Expiration date

- Maturity date

Here is what I have tried so far:

```
 # QuantLib date settings
        todayDate = ql.Date().todaysDate()
        ql.Settings.instance().evaluationDate = todayDate
        dayCount = ql.ActualActual()

        volatilityHandle = ql.BlackVolTermStructureHandle(
            ql.BlackConstantVol(todayDate, ql.NullCalendar(), ql.QuoteHandle(ql.SimpleQuote(req.Volatility)), dayCount)
        )

        # define the option
        option = ql.VanillaOption(
            ql.PlainVanillaPayoff(ql.Option.Call, req.StrikePrice), ql.AmericanExercise(req.ExpirationDate)
        )

        # build the process
        localInterestRateHandle = ql.YieldTermStructureHandle(
            ql.FlatForward(todayDate, req.LocalInterestRate / 100, dayCount)
        )
        # this is the one big issue - for FX options, both the domestic and 
        # foreign risk-free rates have to be considered, but the 
        # BlackScholesProcess does not cater for this.
        # foreignInterestRateHandle = ql.YieldTermStructureHandle(
        #     ql.FlatForward(todayDate, req.ForeignInterestRate / 100, dayCount)
        # )
        process = ql.BlackScholesProcess(
            ql.QuoteHandle(ql.SimpleQuote(req.SpotPrice + req.ForwardPoints)),
            localInterestRateHandle,
            volatilityHandle,
        )

        # set the pricing engine
        option.setPricingEngine(ql.MCAmericanEngine(process, "pseudorandom", timeSteps=100, requiredSamples=1000))
        return GetOptionValueResponse(OptionValue=option.NPV())
```

The biggest issue is that (as commented), the `BlackScholesProcess` does not cater for domestic and foreign RFRs. The `GarmanKohlagenProcess` does work for European options, but I have not seen a similar process for American options.

For more clarity, this is the request class passed to the function:

```

@dataclass
class GetOptionValueRequest:
    """The request parameters of the GetEuropeanOptionValue service."""

    SpotPrice: float
    """The latest spot price of the underlying asset."""
    ForwardPoints: float
    """The latest forward points of the underlying asset."""
    StrikePrice: float
    """The strike price of the option."""
    ExpirationDate: date
    """The expiration date of the option in days."""
    LocalInterestRate: float
    """The risk-free interest rate of the local currency as a percentage"""
    ForeignInterestRate: float
    """The risk-free interest rate of the foreign currency as a percentage"""
    Volatility: float
    """The volatility of the underlying asset"""
```

## Answer by Luigi Ballabio (score 3, accepted)

https://quant.stackexchange.com/a/76217

The `GarmanKohlagenProcess` manages both foreign and domestic curves and can be passed to every engine that asks for a `GeneralizedBlackScholesProcess`.

I wouldn't choose `MCAmericanEngine`, though; it uses a Longstaff-Schwarz simulation, and you probably don't need that complexity and the calculation time it requires. You're probably better off using either a binomial tree (with an engine like `BinomialCRRVanillaEngine`, or one of the others listed here) or a finite-difference model (with the `FdBlackScholesVanillaEngine`).

However, I'm afraid that `VanillaOption` only models spot options.

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.