hasquant-0.5.0.2: test/example/QuantLib/Example/Replication.hs
{-# LANGUAGE OverloadedLists #-}
module QuantLib.Example.Replication
(
Result(..)
, run
) where
import Control.Monad(void, foldM)
import Data.Time.Calendar
import QuantLib.Instrument
import QuantLib.Instrument.Option
import QuantLib.InterestRate
import QuantLib.Quote
import QuantLib.PricingEngine
import QuantLib.Process
import QuantLib.Settings
import QuantLib.TermStructure.Volatility
import QuantLib.TermStructure.Yield
import QuantLib.Time.Calendar
import QuantLib.Time.Date
import QuantLib.Time.Schedule
data Result = Result
{ npvInit :: [Double]
, npvOut :: [Double]
, npvIn :: [Double]
} deriving Show
run :: IO Result
run = do
setEvaluationDate $ Just tod
underlyingQuote <- simpleQuote initialSpot
riskFreeRate <- simpleQuote 0.04
vol <- simpleQuote 0.20
dc <- dayCounter Actual365FixedStandard
cal <- calendar Null
flatRate <- flatForward' 0 cal riskFreeRate dc Continuous Annual
flatVol <- blackConstantVol' 0 cal vol dc
let ex = European $ EuropeanExercise maturity
payoff = PlainVanilla $ PlainVanillaPayoff optionType strike
bsProcess <- blackScholesProcess underlyingQuote flatRate flatVol EulerDiscretization False
barrierEngine <- analyticBarrierEngine bsProcess
europeanEngine <- analyticEuropeanEngine bsProcess Nothing
referenceOption <- barrierOption barrierType barrier rebate payoff ex
refInstrument <- asOneAssetOption referenceOption >>= asOption >>= asInstrument
setPricingEngine referenceOption barrierEngine
put1 <- europeanOption payoff ex >>= asOneAssetOption >>= asOption >>= asInstrument
setPricingEngine put1 europeanEngine
digitalPut <- europeanOption (CashOrNothing Put barrier 1.0) ex >>= asOneAssetOption >>= asOption >>= asInstrument
setPricingEngine digitalPut europeanEngine
put2 <- europeanOption (PlainVanilla $ PlainVanillaPayoff Put barrier) ex >>= asOneAssetOption >>= asOption >>= asInstrument
setPricingEngine put2 europeanEngine
let p = [(put1, 1), (digitalPut, barrier-strike), (put2, -1)]
portfolio1 <- foldM (addInstrument europeanEngine underlyingQuote) p
(zip maturities1 killDates1) >>= composite
portfolio2 <- foldM (addInstrument europeanEngine underlyingQuote) p
(zip maturities2 killDates2) >>= composite
portfolio3 <- foldM (addInstrument europeanEngine underlyingQuote) p
(zip maturities3 killDates3) >>= composite
setEvaluationDate $ Just tod
-- naming the three spots directly, rather than pattern-matching a 3-element list out
-- of a mapM over `underlyingValues`, which hid which result belonged to which spot
-- the signature is load-bearing: OverloadedLists is on for this module, so without
-- it the instrument list literal makes the inferred type IsList-polymorphic, which
-- GHC 8.10 rejects outright ("illegal equational constraint")
let npvsAtSpot :: Double -> IO [Double]
npvsAtSpot v = setValue underlyingQuote v
>> mapM npv [refInstrument, portfolio1, portfolio2, portfolio3]
npvAtInitial <- npvsAtSpot initialSpot
npvAtOutOfTheMoney <- npvsAtSpot outOfTheMoneySpot
npvAtInTheMoney <- npvsAtSpot inTheMoneySpot
return Result {
npvInit = npvAtInitial
, npvOut = npvAtOutOfTheMoney
, npvIn = npvAtInTheMoney
}
where barrierType = DownOut
optionType = Put
tod = 29 `may` 2006
maturity = addGregorianYearsClip 1 tod
barrier = 70.0
rebate = 0.0
initialSpot, outOfTheMoneySpot, inTheMoneySpot :: Double
initialSpot = 100.0
outOfTheMoneySpot = 110.0
inTheMoneySpot = 90.0
strike = 100.0
i1 = [12, 11 .. 1]
maturities1 = map (`addGregorianMonthsClip` tod) i1
killDates1 = map (\i -> addGregorianMonthsClip (i-1) tod) i1
i2 = [52, 50 .. 2]
maturities2 = map (\i -> addDays (i*7) tod) i2
killDates2 = map (\i -> addDays ((i-2)*7) tod) i2
i3 = [52, 51 .. 1]
maturities3 = map (\i -> addDays (i*7) tod) i3
killDates3 = map (\i -> addDays ((i-1)*7) tod) i3
addInstrument engine underlyingQuote pp (m, k) = do
(p, r) <- nextComponent engine underlyingQuote pp m k
return $ (p, r) : pp
nextComponent engine underlyingQuote p innerMaturity killDate = do
let innerExercise = European $ EuropeanExercise innerMaturity
innerPayoff = PlainVanilla $ PlainVanillaPayoff Put barrier
putn <- europeanOption innerPayoff innerExercise >>= asOneAssetOption >>= asOption >>= asInstrument
setPricingEngine putn engine
setEvaluationDate $ Just killDate
void $ setValue underlyingQuote barrier
portfolioValue <- composite p >>= npv
putValue <- npv putn
return (putn, -portfolioValue / putValue)
-- vim: set ft=haskell ff=unix ts=8 sts=2 sw=2 et: