packages feed

hasquant-0.5.0.2: test/example/QuantLib/Example/ConvertibleBond.hs

{-# LANGUAGE TemplateHaskell #-}
module QuantLib.Example.ConvertibleBond
  (
    Result(..)
  , run
  ) where
import Control.Monad(zipWithM)
import Data.Time.Calendar

import qualified QuantLib.CashFlow as CF
import QuantLib.Instrument
import QuantLib.InterestRate
import QuantLib.Instrument.Bond
import QuantLib.Instrument.Option
import QuantLib.Math
import QuantLib.Quote
import QuantLib.PricingEngine
import QuantLib.Process
import QuantLib.Settings
import QuantLib.TermStructure.Yield
import QuantLib.TermStructure.Volatility
import QuantLib.Time.Calendar
import QuantLib.Time.Date
import QuantLib.Time.Schedule
import QuantLib.Syntax

data Result = Result
  { jarrowRuddR :: [Double]
  , coxRossRubinsteinR :: [Double]
  , additiveEQPBinomialTreeR :: [Double]
  , trigeorgisR :: [Double]
  , tianR :: [Double]
  , leisenReimerR :: [Double]
  , joshiR :: [Double]
  }

run :: IO Result
run = do
  cal <- calendar TARGET
  tod <- adjust cal (6 `november` 2013) Following
  setEvaluationDate $ Just tod
  settl <- advance cal tod (fromIntegral settlementDays, Days) Following False
  exec <- advance cal settl (len, Years) Following False
  issue <- advance cal exec (-len, Years) Following False

  sched <- schedule (Just issue) exec (1, Years) cal ModifiedFollowing ModifiedFollowing Backward False Nothing Nothing
  bdc <- dayCounter Thirty360BondBasis
  schedDates <- dates sched

  let callPrices = map (\x -> Soft (x, Clean)) callPricesV
      putPrices = map (\x -> Callability (x, Clean)) putPricesV
      -- schedDates comes back from C++, so its length is not known statically; report
      -- a short schedule rather than letting `!!` throw an index-too-large exception
      schedDateAt y = case drop y schedDates of
        (d : _) -> pure d
        [] -> fail $ "convertible bond: callability at schedule index " ++ show y
                       ++ " requested, but the schedule has only "
                       ++ show (length schedDates) ++ " dates"
  callability1 <- zipWithM (\pp y -> (`pp` 1.20) <$> schedDateAt y) callPrices callLength
  callability2 <- zipWithM (\pp y -> pp CallabilityPut <$> schedDateAt y) putPrices putLength
  let callabilities = callability1 ++ callability2

  let divDates = [d | m <- [6, 12 .. 1000], let d = addGregorianMonthsClip m tod, d < exec]
  dividends <- mapM (CF.fixedDividend 1.0) divDates
  dc <- dayCounter Actual365FixedStandard

  riskFreeQ <- simpleQuote riskFreeRate
  divQ <- simpleQuote dividendYield
  volQ <- simpleQuote vol
  creditSpreadQ <- simpleQuote spreadRate

  ts <- flatForward settl riskFreeQ dc Continuous Annual
  dts <- flatForward settl divQ dc Continuous Annual
  vts <- blackConstantVol settl cal volQ dc

  bsmProc <- simpleQuote under >>= $(free1st 'blackScholesMertonProcess) dts ts vts EulerDiscretization False

  let euEx = European $ EuropeanExercise exec
      amEx = American (Just settl) exec False
  euBond <- convertibleFixedCouponBond euEx conversionRatio callabilities issue settlementDays coupons bdc sched redemption (0, Days) cal Unadjusted False
  amBond <- convertibleFixedCouponBond amEx conversionRatio callabilities issue settlementDays coupons bdc sched redemption (0, Days) cal Unadjusted False

  [jr, crr, ad, tr, ti, lr, j] <- mapM
    (priceBonds euBond amBond bsmProc creditSpreadQ dividends)
    [JarrowRudd, CoxRossRubinstein, AdditiveEQPBinomialTree, Trigeorgis, Tian, LeisenReimer, Joshi4]

  return Result {
      jarrowRuddR = jr
    , coxRossRubinsteinR = crr
    , additiveEQPBinomialTreeR = ad
    , trigeorgisR = tr
    , tianR = ti
    , leisenReimerR = lr
    , joshiR = j
  }
  where under = 36.0
        spreadRate = 0.005
        dividendYield = 0.02
        riskFreeRate = 0.06
        vol = 0.20
        settlementDays = 3
        len = 5
        redemption = 100.0
        conversionRatio = redemption/under -- at the money
        timeSteps = 801
        coupons = [0.05]
        callLength = [2, 4] -- Call dates, years 2, 4.
        putLength = [3] -- Put dates year 3
        callPricesV = [101.5, 100.85]
        putPricesV = [105.0]

        priceBonds eu am p cs d b = do
          eng1 <- binomialConvertibleEngine b p timeSteps cs d
          eng2 <- binomialConvertibleEngine b p timeSteps cs d
          setPricingEngine eu eng1
          setPricingEngine am eng2
          mapM npv [eu, am]

-- vim: set ft=haskell ff=unix ts=8 sts=2 sw=2 et: