packages feed

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

module QuantLib.Example.EquityTotalReturnSwap
  (
    Result(..)
  , run
  ) where

import QuantLib.Currency
import QuantLib.Index(addFixing)
import QuantLib.Index.Equity(equityIndex)
import qualified QuantLib.Index.InterestRate as IR
import QuantLib.Instrument
import QuantLib.Instrument.Swap
import qualified QuantLib.InterestRate as IR2
import QuantLib.PricingEngine
import QuantLib.Quote(simpleQuote)
import QuantLib.Settings(setEvaluationDate)
import QuantLib.TermStructure.Yield(flatForward)
import QuantLib.Time.Calendar
import QuantLib.Time.Date hiding(today)
import QuantLib.Time.Schedule

-- |Port of QuantLib's test-suite/equitytotalreturnswap.cpp testFairMargin: build a
-- total-return swap struck at a zero margin, read off its fair margin, then rebuild it
-- at that fair margin and confirm the resulting NPV is ~0 -- an internal-consistency
-- check, since upstream's own test asserts the same invariant rather than a golden NPV.
data Result = Result
  { fairMarginIborR :: Double
  , parNpvIborR :: Double
  , fairMarginOvernightR :: Double
  , parNpvOvernightR :: Double
  } deriving Show

run :: IO Result
run = do
  cal <- calendar UnitedStatesGovernmentBond
  today <- adjust cal (27 `january` 2023) Following
  setEvaluationDate $ Just today
  dc <- dayCounter Actual365FixedStandard
  usd <- currency USD

  interestQ <- simpleQuote 0.0375
  dividendQ <- simpleQuote 0.005
  interestCurve <- flatForward today interestQ dc IR2.Continuous Annual
  dividendCurve <- flatForward today dividendQ dc IR2.Continuous Annual

  spotQ <- simpleQuote 8700.0
  eqIndex <- equityIndex "eqIndex" cal usd (Just interestCurve) (Just dividendCurve) (Just spotQ)
  addFixing eqIndex (5 `january` 2023) 9010.0 False
  addFixing eqIndex today 8690.0 False

  sofr <- IR.overnightIborIndex IR.Sofr (Just interestCurve)
  mapM_ (\(d, r) -> addFixing sofr d r False) sofrFixings

  usdLibor <- IR.iborIndex (IR.Libor "USDLibor" (3, Months) 2 usd cal dc) (Just interestCurve)
  addFixing usdLibor (3 `january` 2023) 0.035 False

  engine <- discountingSwapEngine interestCurve Nothing Nothing Nothing

  sched <- schedule (Just (5 `january` 2023)) (5 `april` 2023) (3, Months) cal
    Following Following Backward False Nothing Nothing

  (fairMarginIbor, parNpvIbor) <- checkFairMargin engine
    (\m -> equityTotalReturnSwapIbor Receiver nominal sched eqIndex usdLibor dc m 1.0 cal Following 0)
  (fairMarginOvernight, parNpvOvernight) <- checkFairMargin engine
    (\m -> equityTotalReturnSwapOvernight Receiver nominal sched eqIndex sofr dc m 1.0 cal Following 0)

  return Result
    { fairMarginIborR = fairMarginIbor
    , parNpvIborR = parNpvIbor
    , fairMarginOvernightR = fairMarginOvernight
    , parNpvOvernightR = parNpvOvernight
    }
  where
    nominal = 1.0e7
    sofrFixings =
      [ (3 `january` 2023, 0.030), (4 `january` 2023, 0.031), (5 `january` 2023, 0.031)
      , (6 `january` 2023, 0.031), (9 `january` 2023, 0.032), (10 `january` 2023, 0.033)
      , (11 `january` 2023, 0.033), (12 `january` 2023, 0.033), (13 `january` 2023, 0.033)
      , (17 `january` 2023, 0.033), (18 `january` 2023, 0.034), (19 `january` 2023, 0.034)
      , (20 `january` 2023, 0.034), (23 `january` 2023, 0.034), (24 `january` 2023, 0.034)
      , (25 `january` 2023, 0.034), (26 `january` 2023, 0.034)
      ]

    checkFairMargin :: PricingEngine -> (Double -> IO EquityTotalReturnSwap) -> IO (Double, Double)
    checkFairMargin engine build = do
      trs0 <- build 0.0
      setPricingEngine trs0 engine
      fm <- fairMargin trs0
      parTrs <- build fm
      setPricingEngine parTrs engine
      parNpv <- npv parTrs
      return (fm, parNpv)

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