Pricing Cash-or-Nothing Binary Options with QuantLib
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.