packages feed

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

module QuantLib.Example.CVAIRS
  (
    SwapRow(..)
  , Result(..)
  , run
  ) where
import Control.Monad(forM)

import qualified QuantLib.Index.InterestRate as IR
import QuantLib.Instrument
import QuantLib.Instrument.Swap
import QuantLib.Math
import QuantLib.PricingEngine
import QuantLib.Quote
import qualified QuantLib.TermStructure.Credit as Credit
import qualified QuantLib.TermStructure.Yield as TS
import QuantLib.Time.Calendar
import QuantLib.Time.Date
import QuantLib.Time.Schedule
import QuantLib.Settings

-- |Reproduces Table 2 on page 11 of "A Formula for Interest Rate Swaps
-- Valuation under Counterparty Risk in presence of Netting Agreements"
-- (Brigo, Masetti; 2005).
data SwapRow = SwapRow
  { tenorR :: Int
  , fairRateR :: Double
  , lowCorrectionBp :: Double    -- ^low-risk CVA correction, in bp
  , mediumCorrectionBp :: Double -- ^medium-risk CVA correction, in bp
  , highCorrectionBp :: Double   -- ^high-risk CVA correction, in bp
  } deriving Show

newtype Result = Result { rowsR :: [SwapRow] } deriving Show

run :: IO Result
run = do
  cal <- calendar TARGET
  todaysDate <- adjust cal (10 `march` 2004) Following
  setEvaluationDate (Just todaysDate)

  actActISDA <- dayCounter ActualActualISDA
  act360 <- dayCounter (Actual360 False)

  yieldIndex <- IR.iborIndex IR.Euribor3M Nothing

  swapQuotes <- mapM simpleQuote ratesSwapMkt
  swapHelpers <- forM (zip swapQuotes tenorsSwapMkt) $ \(q, t) ->
    TS.swapRateHelper' q (t, Years) cal Quarterly ModifiedFollowing actActISDA yieldIndex
      Nothing (0, Days) Nothing
      Nothing TS.LastRelevantDate Nothing False Nothing Nothing Nothing
      >>= TS.asRateHelper

  swapTS <- TS.piecewiseYieldCurve' 2 cal swapHelpers actActISDA [] TS.Discount LogLinear True

  riskFreeEngine <- discountingSwapEngine swapTS Nothing Nothing Nothing

  defaultDates <- mapM (\m -> advance cal todaysDate (m, Months) Following False) defaultTenorsMonths
  let mkHazardCurve intensities =
        Credit.interpolatedHazardRateCurve (zip defaultDates intensities) act360 cal [] BackwardFlat True
  lowTS <- mkHazardCurve intensitiesLow
  mediumTS <- mkHazardCurve intensitiesMedium
  highTS <- mkHazardCurve intensitiesHigh

  blackVolQuote <- simpleQuote blackVol
  ctptyLow <- counterpartyAdjSwapEngine swapTS blackVolQuote lowTS ctptyRRLow Nothing 0.999
  ctptyMedium <- counterpartyAdjSwapEngine swapTS blackVolQuote mediumTS ctptyRRMedium Nothing 0.999
  ctptyHigh <- counterpartyAdjSwapEngine swapTS blackVolQuote highTS ctptyRRHigh Nothing 0.999

  yieldIndexS <- IR.iborIndex IR.Euribor3M (Just swapTS)

  rows <- forM (zip tenorsSwapMkt ratesSwapMkt) $ \(t, r) -> do
    riskySwap <- makeVanillaSwap (fromIntegral t, Years) yieldIndexS r (0, Days) (Just 2)
      (3, Months) actActISDA (Just ModifiedFollowing) (Just ModifiedFollowing)
      (Just cal) (Just cal) (Just 100.0) (Just Payer)

    setPricingEngine riskySwap riskFreeEngine
    nonRiskyFair <- fairRate riskySwap

    setPricingEngine riskySwap ctptyLow
    lowFair <- fairRate riskySwap
    setPricingEngine riskySwap ctptyMedium
    mediumFair <- fairRate riskySwap
    setPricingEngine riskySwap ctptyHigh
    highFair <- fairRate riskySwap

    pure SwapRow
      { tenorR = t
      , fairRateR = nonRiskyFair
      , lowCorrectionBp = 10000 * (lowFair - nonRiskyFair)
      , mediumCorrectionBp = 10000 * (mediumFair - nonRiskyFair)
      , highCorrectionBp = 10000 * (highFair - nonRiskyFair)
      }

  pure (Result rows)

  where
    tenorsSwapMkt = [5, 10, 15, 20, 25, 30] :: [Int]
    ratesSwapMkt = [0.03249, 0.04074, 0.04463, 0.04675, 0.04775, 0.04811]
    defaultTenorsMonths = [0, 12, 36, 60, 84, 120, 180, 240, 300, 360] :: [Int]
    -- three risk levels (trailing element unused, matching the C++ example's
    -- own array-vs-loop-bound mismatch)
    intensitiesLow = [0.0036, 0.0036, 0.0065, 0.0099, 0.0111, 0.0177, 0.0177, 0.0177, 0.0177, 0.0177, 0.0177]
    intensitiesMedium = [0.0202, 0.0202, 0.0231, 0.0266, 0.0278, 0.0349, 0.0349, 0.0349, 0.0349, 0.0349, 0.0349]
    intensitiesHigh = [0.0534, 0.0534, 0.0564, 0.06, 0.0614, 0.0696, 0.0696, 0.0696, 0.0696, 0.0696, 0.0696]
    ctptyRRLow = 0.4
    ctptyRRMedium = 0.35
    ctptyRRHigh = 0.3
    blackVol = 0.15

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