packages feed

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

module QuantLib.Example.Swap
  (
    Result(..)
  , SwapResult(..)
  , IterationResult(..)
  , run
  ) where
import Control.Monad(void, forM)
import Data.Time.Calendar

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

data SwapResult = SwapResult { spotNpvR :: Double
                              , spotFairSpreadR :: Double
                              , spotFairRateR :: Double
                              } deriving Show

data IterationResult = IterationResult {spotSwap :: SwapResult, forwardSwap :: SwapResult} deriving Show

data Result = Result [IterationResult] [IterationResult] deriving Show

run :: IO Result
run = do
  cal <- calendar TARGET
  settleDate <- adjust cal settleDate1 Following
  advance cal settleDate (-fixingDays, Days) Following False >>= setEvaluationDate . Just

  depoQuotes <- forM depoRates simpleQuote
  fraQuotes <- forM fraRates simpleQuote
  futQuotes <- forM futPrices simpleQuote
  swapQuotes <- forM swapRates simpleQuote

  depoDC <- dayCounter (Actual360 False)

  depoHelpers <- mapM (\(q, p) ->
    TS.depositRateHelper q p (fromIntegral fixingDays) cal ModifiedFollowing True depoDC) $
      zip depoQuotes depoTerms
  fraHelpers <- mapM (\(q, (m1, m2)) ->
    TS.fraRateHelper q m1 m2 (fromIntegral fixingDays) cal ModifiedFollowing True depoDC TS.LastRelevantDate Nothing True) $
      zip fraQuotes fraTerms

  imm1 <- nextIMMDate settleDate True
  -- chain of IMM dates, each derived from the one before. Written as an explicit
  -- unfold rather than foldM over an accumulator list, which needed a partial `last`
  -- to see the previous date and rebuilt the list with `++` on every step.
  let nextIMMs :: Int -> Day -> IO [Day]
      nextIMMs 0 _ = pure []
      nextIMMs k prev = do
        v <- nextIMMDate (addDays 1 prev) True
        (v :) <$> nextIMMs (k - 1) v
  imms <- (imm1 :) <$> nextIMMs (length futPrices - 1) imm1

  futHelpers <- mapM (\(q, imm) ->
    TS.futuresRateHelper q imm 3 cal ModifiedFollowing True depoDC Nothing TS.IMM) $
      zip futQuotes imms

  swFixedDC <- dayCounter Thirty360European
  eu6m <- IR.iborIndex IR.Euribor6M Nothing
  swapHelpers <- mapM (\(q, y) ->
    TS.swapRateHelper' q (y, Years) cal Annual Unadjusted swFixedDC eu6m Nothing (0, Days) Nothing
      Nothing TS.LastRelevantDate Nothing False Nothing Nothing Nothing >>= TS.asRateHelper) $
      zip swapQuotes swapYears

  tsDC <- dayCounter ActualActualISDA

  depoSwapTS <- TS.piecewiseYieldCurve settleDate (depoHelpers++swapHelpers) tsDC [] TS.Discount LogLinear
  depoFutSwapTS <- TS.piecewiseYieldCurve settleDate (take 2 depoHelpers++futHelpers++drop 1 swapHelpers) tsDC [] TS.Discount LogLinear
  depoFraSwapTS <- TS.piecewiseYieldCurve settleDate (take 3 depoHelpers++fraHelpers++swapHelpers) tsDC [] TS.Discount LogLinear

  i1 <- forM [depoSwapTS, depoFutSwapTS, depoFraSwapTS] (\ts -> valuateSwap settleDate ts ts)

  let market5YQuote = swapQuotes !! 2
  void $ setValue market5YQuote 0.0460

  i2 <- forM [depoSwapTS, depoFutSwapTS, depoFraSwapTS] (\ts -> valuateSwap settleDate ts ts)

  return $ Result i1 i2

  where
    settleDate1 = 22 `september` 2004
    fixingDays = 2

    depoRates = [0.0382, 0.0372, 0.0363, 0.0353, 0.0348, 0.0345]
    fraRates = [0.037125, 0.037125, 0.037125]
    futPrices = [96.2875, 96.7875, 96.9875, 96.6875, 96.4875, 96.3875, 96.2875, 96.0875]
    swapRates = [0.037125, 0.0398, 0.0443, 0.05165, 0.055175]
    depoTerms = [(1, Weeks), (1, Months), (3, Months), (6, Months), (9, Months), (1, Years)]
    fraTerms = [(3, 6), (6, 9), (6, 12)]
    swapYears = [2, 3, 5, 10, 15]

    valuateSwap :: Day -> TS.GenYieldTermStructure y1 -> TS.GenYieldTermStructure y2 -> IO IterationResult
    valuateSwap settle d f = do
      fixDC <- dayCounter Thirty360European
      floatDC <- dayCounter (Actual360 False)
      eu6m <- IR.iborIndex IR.Euribor6M (Just f)
      fixP <- fromFrequency Annual
      floatP <- fromFrequency Semiannual
      cal <- calendar TARGET
      let maturity = addGregorianYearsClip 5 settle
      fixSched <- schedule (Just settle) maturity fixP cal Unadjusted Unadjusted Forward False Nothing Nothing
      floatSched <- schedule (Just settle) maturity floatP cal ModifiedFollowing ModifiedFollowing Forward False Nothing Nothing
      spot5Y <- vanillaSwap Payer 1000000 fixSched 0.04 fixDC
        floatSched eu6m 0.0 floatDC (Just ModifiedFollowing) Nothing

      fwdStart <- advance cal settle (1, Years) Following False
      let fwdMat = addGregorianYearsClip 5 fwdStart
      fwdFixS <- schedule (Just fwdStart) fwdMat fixP
        cal Unadjusted Unadjusted Forward False Nothing Nothing
      fwdFloatS <- schedule (Just fwdStart) fwdMat floatP
        cal ModifiedFollowing ModifiedFollowing Forward False Nothing Nothing
      fwd1Y5Y <- vanillaSwap Payer 1000000 fwdFixS 0.04 fixDC
        fwdFloatS eu6m 0.0 floatDC (Just ModifiedFollowing) Nothing
      refDate <- referenceDate d
      pricer <- discountingSwapEngine d (Just False) (Just refDate) (Just refDate)

      setPricingEngine spot5Y pricer
      setPricingEngine fwd1Y5Y pricer

      spotNPV <- npv spot5Y
      spotFairSpread <- fairSpread spot5Y
      spotFairRate <- fairRate spot5Y
      let sr = SwapResult spotNPV spotFairSpread spotFairRate

      fwdNPV <- npv fwd1Y5Y
      fwdFairSpread <- fairSpread fwd1Y5Y
      fwdFairRate <- fairRate fwd1Y5Y
      let fr = SwapResult fwdNPV fwdFairSpread fwdFairRate
      return $ IterationResult sr fr

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