Skip to content
All library documents

Pricing Cash-or-Nothing Binary Options with QuantLib

Article Quant Q&A · Author: Desi_Quant

Summary

The document addresses how to represent a binary call payoff that pays a fixed amount when the underlying finishes above the strike, in a QuantLib European option priced with Monte Carlo simulation. The question’s example mistakenly uses a plain vanilla call payoff, which pays the amount by which the underlying exceeds the strike, rather than a fixed cash amount.

The answer replaces that payoff with QuantLib’s cash-or-nothing payoff type, then uses the same option setup with Monte Carlo and analytic pricing engines. The example reports close but non-identical prices from the two engines, illustrating a comparison in one parameterized setup. It does not explain the Heaviside function directly, quantify simulation error, or explore convergence and parameter sensitivity; the price comparison alone is not a general validation of the implementation.

Key ideas

  • A cash-or-nothing call pays a fixed amount when the underlying price exceeds the strike.
  • A plain vanilla call payoff does not represent a binary option because its payout grows with the amount above the strike.
  • QuantLib provides a cash-or-nothing payoff type for specifying the binary payoff.
  • The example prices the option with both Monte Carlo and analytic engines, with slightly different reported estimates.

Tags

Full text
# Issue in Pricing Binary Options using Heaviside Function and QuantLib Python


# Issue in Pricing Binary Options using Heaviside Function and QuantLib Python












I am trying to price binary option using MC Simulation and Python QuantLib Library. The price of the option matches with the Analytical Engine. However, I am not sure how to incorporate the Heaviside Function to calculate the payoff (1 if St > K; 0 otherwise). Here is the code for the same:

```
import QuantLib as ql
today = ql.Date().todaysDate()
initialValue = 40
riskFreeTS = ql.YieldTermStructureHandle(ql.FlatForward(today, 0.01, ql.Actual365Fixed()))
dividendTS = ql.YieldTermStructureHandle(ql.FlatForward(today, 0.02, ql.Actual365Fixed()))
volatility = ql.BlackVolTermStructureHandle(ql.BlackConstantVol(today, ql.NullCalendar(), 0.2, ql.Actual365Fixed()))

process = ql.BlackScholesMertonProcess(ql.QuoteHandle(ql.SimpleQuote(40)), riskFreeTS, dividendTS, volatility)
steps = 2
rng = "pseudorandom" # could use "lowdiscrepancy"
numPaths = 500000
option_type = ql.Option.Call
strike_price = 40

maturity_date = ql.Date(2, 4, 2021)
exercise = ql.EuropeanExercise(maturity_date)
payoff=ql.PlainVanillaPayoff(ql.Option.Call, strike_price)
binary_option = ql.VanillaOption(payoff, exercise)

engine = ql.MCEuropeanEngine(process, rng, steps, requiredSamples=numPaths)
```

## Run with Monte Carlo

```
binary_option.setPricingEngine(engine)
price = binary_option.NPV()
print("Monte Carlo Price: {}".format(price))
```

## Run with Analytic Engine

```
engine = ql.AnalyticEuropeanEngine(process)
binary_option.setPricingEngine(engine)
print("Analytic Price: {}".format(binary_option.NPV()))
```

The output is:

```
Monte Carlo Price: 2.5199444258975885

Analytic Price: 2.5135333959120034
```

## Answer by David Duarte (score 1)

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

You will need to change the payoff from:

```
payoff = ql.PlainVanillaPayoff(ql.Option.Call, strike_price)
```

to

```
payoff = ql.CashOrNothingPayoff(ql.Option.Call, strike_price, 1)
```

## Answer by Desi_Quant (score 0)

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

```
import QuantLib as ql

today = ql.Date().todaysDate()

initialValue = 40

riskFreeTS = ql.YieldTermStructureHandle(ql.FlatForward(today, 0.01, ql.Actual365Fixed()))

dividendTS = ql.YieldTermStructureHandle(ql.FlatForward(today, 0.02, ql.Actual365Fixed()))

volatility = ql.BlackVolTermStructureHandle(ql.BlackConstantVol(today, ql.NullCalendar(), 0.2, ql.Actual365Fixed()))

process = ql.BlackScholesMertonProcess(ql.QuoteHandle(ql.SimpleQuote(40)), riskFreeTS, dividendTS, volatility)

steps = 2

rng = "pseudorandom" # could use "lowdiscrepancy"

numPaths = 500000

option_type = ql.Option.Call

strike_price = 40

maturity_date = ql.Date(2, 4, 2021)

exercise = ql.EuropeanExercise(maturity_date)

payoff = ql.CashOrNothingPayoff(ql.Option.Call, strike_price, 1)

binary_option = ql.VanillaOption(payoff, exercise)

engine = ql.MCEuropeanEngine(process, rng, steps, requiredSamples=numPaths)

#Run with Monte Carlo

binary_option.setPricingEngine(engine)

price = binary_option.NPV()

print("Monte Carlo Price: {}".format(price))

# Run with Analytic Engine

engine = ql.AnalyticEuropeanEngine(process)

binary_option.setPricingEngine(engine)

print("Analytic Price: {}".format(binary_option.NPV()))
```

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.