hasquant-0.7.0.0: test/example/QuantLib/Example/ForwardOption.hs
module QuantLib.Example.ForwardOption
(
Result(..)
, run
) where
import Data.Time.Calendar
import QuantLib.Instrument
import QuantLib.InterestRate
import QuantLib.Instrument.Option
import QuantLib.Math
import QuantLib.PricingEngine
import QuantLib.Process
import QuantLib.Quote
import QuantLib.Context
import QuantLib.Time.Calendar
import QuantLib.Time.Date
import QuantLib.Time.Schedule
import QuantLib.TermStructure.Volatility
import QuantLib.TermStructure.Yield
-- | Forward-starting (\"cliquet-style\") vanilla options, reproducing
-- QuantLib's own @forwardoption.cpp@ @testValues@ golden case (Haug,
-- \"Option pricing formulas\", p.37\/VBA code): moneyness 1.1, spot 60,
-- dividend yield 4%, risk-free rate 8%, reset in 0.25y, maturity in 1y,
-- 30% vol; expected NPV 4.4064 (call) \/ 8.2971 (put) under
-- 'forwardEuropeanEngine' (the @AnalyticEuropeanEngine@ instantiation of
-- @ForwardVanillaEngine\<Engine\>@). The other 5 forward-starting engines
-- added alongside it are exercised as self-consistency checks against this
-- reference: the finite-differences and Monte Carlo European forward
-- engines should reproduce the same (European-exercise) price; the two
-- American approximation engines, priced on an otherwise identical
-- American-exercise instrument, should be at least as valuable as the
-- European price (the right to exercise early is never a disadvantage);
-- and the Heston forward engine, with its process parameters chosen so
-- Heston degenerates to (near-)deterministic Black-Scholes volatility,
-- should reproduce the same price as the BS analytic engine.
data Result = Result
{ europeanCallR :: Double
, europeanPutR :: Double
, fdEuropeanR :: Double
, mcEuropeanR :: Double
, hestonEuropeanR :: Double
, bawAmericanR :: Double
, bjsAmericanR :: Double
}
run :: IO Result
run = do
setEvaluationDate $ Just evalDate
dc <- dayCounter (Actual360 False)
spotQ <- simpleQuote spot
qQ <- simpleQuote divYield
rQ <- simpleQuote riskFreeRate
qTS <- flatForward (ReferenceDate evalDate) qQ dc Continuous Annual
rTS <- flatForward (ReferenceDate evalDate) rQ dc Continuous Annual
volQ <- simpleQuote vol
volTS <- calendar TARGET >>= \cal -> blackConstantVol (CalendarReferenceDate evalDate) cal volQ dc
bsmProc <- blackScholesMertonProcess spotQ qTS rTS volTS EulerDiscretization False
let payoff t = PlainVanilla $ PlainVanillaPayoff t 0.0
europeanExercise = European $ EuropeanExercise maturity
americanExercise = American Nothing maturity False
callOpt <- forwardVanillaOption moneyness resetDate (payoff Call) europeanExercise
putOpt <- forwardVanillaOption moneyness resetDate (payoff Put) europeanExercise
europeanEng <- forwardEuropeanEngine bsmProc
QuantLib.Instrument.setPricingEngine callOpt europeanEng
europeanCall <- npv callOpt
QuantLib.Instrument.setPricingEngine putOpt europeanEng
europeanPut <- npv putOpt
fdEng <- forwardFdBlackScholesVanillaEngine bsmProc
QuantLib.Instrument.setPricingEngine callOpt fdEng
fdEuropean <- npv callOpt
mcEng <- mcForwardEuropeanBsEngine LowDiscrepancy Statistics bsmProc (Just 1) Nothing False False (Just 32768) Nothing Nothing 0
QuantLib.Instrument.setPricingEngine callOpt mcEng
mcEuropean <- npv callOpt
hestonProc <- hestonProcess rTS (Just qTS) spotQ (vol*vol) 1.0 (vol*vol) 0.3 0.0 QuadraticExponentialMartingale
hestonEng <- analyticHestonForwardEuropeanEngine hestonProc 144
QuantLib.Instrument.setPricingEngine callOpt hestonEng
hestonEuropean <- npv callOpt
americanOpt <- forwardVanillaOption moneyness resetDate (payoff Call) americanExercise
bawEng <- forwardBaroneAdesiWhaleyEngine bsmProc
QuantLib.Instrument.setPricingEngine americanOpt bawEng
bawAmerican <- npv americanOpt
bjsEng <- forwardBjerksundStenslandEngine bsmProc
QuantLib.Instrument.setPricingEngine americanOpt bjsEng
bjsAmerican <- npv americanOpt
return Result
{ europeanCallR = europeanCall
, europeanPutR = europeanPut
, fdEuropeanR = fdEuropean
, mcEuropeanR = mcEuropean
, hestonEuropeanR = hestonEuropean
, bawAmericanR = bawAmerican
, bjsAmericanR = bjsAmerican
}
where
evalDate = 1 `january` 2020
moneyness = 1.1
spot = 60
divYield = 0.04
riskFreeRate = 0.08
vol = 0.30
-- matches upstream's `timeEvalDates(t, 360) = lround(t * 360)`
timeEvalDates t = round (t * 360.0)
resetDate = addDays (timeEvalDates (0.25 :: Double)) evalDate
maturity = addDays (timeEvalDates (1.0 :: Double)) evalDate
-- vim: set ft=haskell ff=unix ts=8 sts=2 sw=2 et: