packages feed

hasquant-0.5.0.2: test/exe/QuantLib/MainExample.hs

module Main where

import Control.Monad(forM_, void)
import Text.Printf(printf)
import Data.List(intercalate)

import QuantLib.Settings
import QuantLib.Time.Date

import qualified QuantLib.Example.FRA as FRA
import qualified QuantLib.Example.Bond as Bond
import qualified QuantLib.Example.Swap as SwapExample
import qualified QuantLib.Example.Repo as RepoExample
import qualified QuantLib.Example.FittedBondCurve as BondCurveExample
import qualified QuantLib.Example.BermudanSwaption as BermudanSwaptionExample
import qualified QuantLib.Example.CallableBond as CallableBondExample
import qualified QuantLib.Example.CDS as CDSExample
import qualified QuantLib.Example.ConvertibleBond as ConvertibleBondExample
import qualified QuantLib.Example.EquityOption as EquityOptionExample
import qualified QuantLib.Example.Replication as ReplicationExample
import qualified QuantLib.Example.TARF as TARF
import qualified QuantLib.Example.CVAIRS as CVAIRSExample
import qualified QuantLib.Example.ShortRateModels as ShortRateModelsExample

main :: IO ()
main = do
  putStrLn $ "QuantLib version " ++ version
     ++ ", Boost " ++ boostVersion
  t <- today
  wd <- weekday t
  putStrLn $ "Today is " ++ show wd

  putStrLn "\n*** Bond Example ***"
  br <- keepingSettings' Bond.run
  putStrLn $ "NPV: " ++ show (Bond.npvR br)
  putStrLn $ "Yield: " ++ show (Bond.yieldR br)
  putStrLn $ "Clean price: " ++ show (Bond.cleanPriceR br)
  putStrLn $ "Dirty price: " ++ show (Bond.dirtyPriceR br)
  putStrLn $ "Accrued amount: " ++ show (Bond.accruedAmountR br)
  putStrLn $ "Previous coupon: " ++ show (Bond.previousCoupon br)
  putStrLn $ "Next coupon: " ++ show (Bond.nextCoupon br)
  putStrLn $ "Next coupon date: " ++ show (Bond.nextCouponDate br)
  putStrLn $ "Floater's clean price from yield: " ++ show (Bond.cleanPriceFromYieldR br)
  putStrLn $ "Floater's yield from clean price: " ++ show (Bond.yieldFromCleanPriceR br)
  putStrLn $ "Tradable: " ++ show (Bond.tradable br)
  putStrLn $ "CashFlows: NPV: " ++ show (Bond.cfnpvR br) ++ ", NPV_BPS: " ++ show (Bond.cfnpvbpsR br)
  putStrLn $ "BPS: " ++ show (Bond.bpsR br)

  putStrLn "\n*** Repo Example ***"
  rr <- keepingSettings' $ RepoExample.run True
  putStrLn $ "Underlying bond clean price: " ++ show (RepoExample.cleanPriceR rr)
  putStrLn $ "Underlying bond dirty price: " ++ show (RepoExample.dirtyPriceR rr)
  putStrLn $ "Underlying bond accrued at settlement: " ++ show (RepoExample.accruedAmountSettlement rr)
  putStrLn $ "Underlying bond accrued at delivery:   " ++ show (RepoExample.accruedAmountDelivery rr)
  putStrLn $ "Underlying bond spot income: " ++ show (RepoExample.spotIncomeR rr)
  putStrLn $ "Underlying bond fwd income:  " ++ show (RepoExample.fwdIncomeR rr)
  putStrLn $ "Repo strike: " ++ show (RepoExample.strike rr)
  putStrLn $ "Repo NPV:    " ++ show (RepoExample.npvR rr)
  putStrLn $ "Repo clean forward price: " ++ show (RepoExample.cleanForwardPriceR rr)
  putStrLn $ "Repo dirty forward price: " ++ show (RepoExample.forwardPriceR rr)
  putStrLn $ "Repo implied yield: " ++ show (RepoExample.impliedYieldR rr)
  putStrLn $ "Market repo rate:   " ++ show (RepoExample.zeroRateR rr)

  putStrLn "\n*** FRA Example ***"
  (FRA.Result i1 i2) <- keepingSettings' FRA.run
  printFraIterationResult i1
  putStrLn "* After 100bp shift *"
  printFraIterationResult i2

  putStrLn "\n*** Swap Example ***"
  (SwapExample.Result si1 si2) <- keepingSettings' SwapExample.run
  printSwapIterationResult si1
  putStrLn "***Updating market data***"
  printSwapIterationResult si2

  putStrLn "\n*** FittedBondCurve Example ***"
  (BondCurveExample.Result ss r1 r2 r3 r4) <- keepingSettings' BondCurveExample.run
  putStrLn $ "Bond settlement date: " ++ show ss
  printBondCurveInfo r1
  printBondCurveInfo r2
  printBondCurveInfo r3
  printBondCurveInfo r4

  putStrLn "\n*** Replication Example ***"
  (ReplicationExample.Result npvInit npvOut npvIn) <- keepingSettings' ReplicationExample.run
  void $ printf "%20s %19s %19s %19s %19s\n" "NPV of" "Analytic" "12-day replication" "26-day replication" "52-day replication"
  printDLine "%20s" "Initial" "%20.6f" npvInit
  printDLine "%20s" "Out of the money" "%20.6f" npvOut
  printDLine "%20s" "In the money" "%20.6f" npvIn

  putStrLn "\n*** BermudanSwaption Example ***"
  (BermudanSwaptionExample.Result g2v g2p hwv hwp hw2v hw2p bkv bkp npvA npvO npvI) <- keepingSettings' BermudanSwaptionExample.run
  void $ printf "%25s %8s %8s %8s %8s %8s\n" "Calibrated vols for" "1x5" "2x4" "3x3" "4x2" "5x1"
  printDLine "%25s" "G2" "%9.5f" g2v
  printDLine "%25s" "Hull-White" "%9.5f" hwv
  printDLine "%25s" "Numerical" "%9.5f" hw2v
  printDLine "%25s" "Black-Karasinski" "%9.5f" bkv
  putStrLn ""
  printDoubles "G2 params (a, sigma, b, beta, eta, rho)" g2p
  printDoubles "HW params (a, sigma)" hwp
  printDoubles "Num HW params (a, sigma)" hw2p
  printDoubles "BK params (a, sigma)" bkp
  putStrLn ""
  void $ printf "%15s %13s %13s %13s %13s %13s %13s %13s\n" "NPV of" "G2(tree)" "G2(fdm)" "HW(tree)" "HW(fdm)" "HW(num, tree)" "HW(num, fdm)" "BK"
  printDLine "%15s" "ATM Swaption" "%14.4f" npvA
  printDLine "%15s" "OTM Swaption" "%14.4f" npvO
  printDLine "%15s" "ITM Swaption" "%14.4f" npvI

  putStrLn "\n*** Equity Option Example ***"
  (EquityOptionExample.Result analyticEuro analyticHeston bates baw bjs bin int fd (mcE, mcE2, mcA)) <- EquityOptionExample.run
  void $ printf "%30s   %9s %9s %9s\n" "NPV of" "European" "Bermudan" "American"
  printEquityOptNPVs "Black-Scholes" (europeanOnly analyticEuro)
  printEquityOptNPVs "Heston semi-analytic" (europeanOnly analyticHeston)
  printEquityOptNPVs "Bates semi-analytic" (europeanOnly bates)
  printEquityOptNPVs "Barone-Adesi/Whaley" (americanOnly baw)
  printEquityOptNPVs "Bjerksund/Stensland" (americanOnly bjs)
  printEquityOptNPVs "Integral" (europeanOnly int)
  printEquityOptNPVs "Finite differences" (allExercises fd)
  mapM_ (uncurry printEquityOptNPVs)
    (zip
      ["Binomial Jarrow-Rudd",
        "Binomial Cox-Ross-Rubinstein",
        "Additive equiprobabilities",
        "Binomial Trigeorgis",
        "Binomial Tian",
        "Binomial Leisen-Reimer",
        "Binomial Joshi"]
      (map allExercises bin))
  printEquityOptNPVs "MC (crude)" (europeanOnly [mcE])
  printEquityOptNPVs "QMC (Sobol)" (europeanOnly [mcE2])
  printEquityOptNPVs "MC (longstaff Schwartz)" (americanOnly [mcA])

  putStrLn "\n*** CDS Example ***"
  (CDSExample.Result probs fairSpread npv defNpv cpnNpv) <- keepingSettings' CDSExample.run
  printDoubles "Survival probabilities (1Y, 2Y)" probs
  void $ printf "%15s %15s %15s %15s %15s\n" "" "3M" "6M" "1Y" "2Y"
  printDLine "%15s" "Fair spread" "%16.6f" fairSpread
  printDLine "%15s" "NPV" "%16.5e" npv
  printDLine "%15s" "Default leg NPV" "%16.2f" defNpv
  printDLine "%15s" "Coupon leg NPV" "%16.2f" cpnNpv

  putStrLn "\n*** Callable Bond Example ***"
  (CallableBondExample.Result ps ys) <- keepingSettings' CallableBondExample.run
  void $ printf "%5s   %10s %10s %10s %10s %10s\n" "" "sigma=0.0" "sigma=1.0" "sigma=3.0" "sigma=6.0" "sigma=12.0"
  printDLine "%5s" "Price" "%11.2f" ps
  printDLine "%5s" "Yield" "%11.2f" ys

  putStrLn "\n*** Convertible Bond Example ***"
  (ConvertibleBondExample.Result jr crr ad tr ti lr j) <- keepingSettings' ConvertibleBondExample.run
  void $ printf "%30s %10s %10s\n" "NPV for Tree" "European" "American"
  printDLine "%30s" "Jarrow-Rudd" "%11.6f" jr
  printDLine "%30s" "Cox-Ross-Rubinstein" "%11.6f" crr
  printDLine "%30s" "Additive equiprobabilities" "%11.6f" ad
  printDLine "%30s" "Trigeorgis" "%11.6f" tr
  printDLine "%30s" "Tian" "%11.6f" ti
  printDLine "%30s" "Leisen-Reimer" "%11.6f" lr
  printDLine "%30s" "Joshi" "%11.6f" j

  putStrLn "\n*** Straightforward Monte Carlo pricing of an FX TARF Example ***"
  (TARF.Result tnpv fwds simFwds) <- keepingSettings' TARF.run
  putStrLn $ "NPV: " ++ show tnpv
  putStrLn $ "Forward Rates:           " ++ show fwds
  putStrLn $ "Simulated Forward Rates: " ++ show simFwds

  putStrLn "\n*** CVA IRS Example ***"
  (CVAIRSExample.Result rows) <- keepingSettings' CVAIRSExample.run
  putStrLn "-- Correction in the contract fix rate in bp --"
  void $ printf "%4s %8s %8s %8s %8s\n" "Tenor" "FairRate" "Low" "Medium" "High"
  forM_ rows $ \(CVAIRSExample.SwapRow tt fr lo med hi) ->
    printf "%4d %8.3f %8.2f %8.2f %8.2f\n" tt (fr*100) lo med hi

  putStrLn "\n*** Short Rate Models Example ***"
  srm <- keepingSettings' ShortRateModelsExample.run
  printCalibration "cachedHullWhite" (ShortRateModelsExample.cachedHullWhite srm)
  printCalibration "cachedHullWhiteFixedReversion" (ShortRateModelsExample.cachedHullWhiteFixedReversion srm)
  printCalibration "cachedHullWhite2" (ShortRateModelsExample.cachedHullWhite2 srm)
  let swapDiffs = map (\s -> abs (ShortRateModelsExample.expectedNPV s - ShortRateModelsExample.calculatedNPV s))
        (ShortRateModelsExample.swaps srm)
  printf "swaps: max |expected-calculated| NPV over %d entries: %.6f\n" (length swapDiffs) (maximum swapDiffs)
  forM_ (ShortRateModelsExample.futuresConvexityBias srm) $ \c ->
    printf "futuresConvexityBias: T=%.3f a=%.5f expected=%.7f calculated=%.7f\n"
      (ShortRateModelsExample.convexityT c) (ShortRateModelsExample.convexityA c)
      (ShortRateModelsExample.expectedForward c) (ShortRateModelsExample.calculatedForward c)
  printDiscountCheck "extendedCirDiscountFactor" (ShortRateModelsExample.extendedCirDiscountFactor srm)
  printDiscountCheck "vasicekDiscountFactorSmallMeanReversion" (ShortRateModelsExample.vasicekDiscountFactorSmallMeanReversion srm)

  putStrLn "\nDONE"

  where
    printCalibration :: String -> ShortRateModelsExample.CalibrationResult -> IO ()
    printCalibration label cr = printf "%s: a = %.6f (cached %.6f), sigma = %.6f (cached %.6f), value = %.6f (cached %.6f)\n"
      label (ShortRateModelsExample.calculatedA cr) (ShortRateModelsExample.cachedA cr)
      (ShortRateModelsExample.calculatedSigma cr) (ShortRateModelsExample.cachedSigma cr)
      (ShortRateModelsExample.calculatedValue cr) (ShortRateModelsExample.cachedValue cr)

    printDiscountCheck :: String -> ShortRateModelsExample.DiscountCheck -> IO ()
    printDiscountCheck label dc = printf "%s: expected=%.8f calculated=%.8f\n" label
      (ShortRateModelsExample.expectedDF dc) (ShortRateModelsExample.calculatedDF dc)

    printFraIterationResult :: [FRA.IterationResult] -> IO ()
    printFraIterationResult rs = forM_ rs $ \r ->
      printf "Fwd rate: %.5f Mkt zrate: %.5f NPV: %.5f\n"
        (FRA.fwdRateR r)
        (FRA.zRateR r)
        (FRA.npvR r)

    printSwapIterationResult :: [SwapExample.IterationResult] -> IO ()
    printSwapIterationResult rs = forM_ rs $ \r -> do
      printSwapResult "Spt" $ SwapExample.spotSwap r
      printSwapResult "Fwd" $ SwapExample.forwardSwap r

    printSwapResult :: String -> SwapExample.SwapResult -> IO ()
    printSwapResult t r =
      printf "%s Swap: NPV: %.5f Far spread: %.5f Fair rate: %.5f\n"
        t (SwapExample.spotNpvR r) (SwapExample.spotFairSpreadR r) (SwapExample.spotFairRateR r)

    printBondCurveInfo :: BondCurveExample.Rate -> IO ()
    printBondCurveInfo (BondCurveExample.Rate date iter tenors rates) = do
      void $ printf "Reference date: %s, iterations: " $ show date
      forM_ iter (printf "%d ")
      putStrLn ""
      forM_ (zip tenors rates) (\(t, r) -> do
        void $ printf "Tenor %5.2fY: " t
        forM_ r (printf "%.3f ")
        putStrLn "")
      putStrLn ""

    -- The equity option table has three columns (European, Bermudan, American) but
    -- most engines price only one or two of them. 'Nothing' is the absent cell.
    -- This used to pad with 0.0 and print "N/A" for any value equal to 0.0, which
    -- would have hidden a legitimately-zero NPV.
    europeanOnly, americanOnly, allExercises :: [Double] -> [Maybe Double]
    europeanOnly v = map Just v ++ [Nothing, Nothing]
    americanOnly v = [Nothing, Nothing] ++ map Just v
    allExercises = map Just

    printEquityOptNPVs :: String -> [Maybe Double] -> IO ()
    printEquityOptNPVs m v = do
      void $ printf "%30s: " m
      mapM_ (maybe (printf " %9s" "N/A") (printf " %9.6f")) v
      putStrLn ""

    printDoubles :: String -> [Double] -> IO ()
    printDoubles m l = printf "%s: %s\n" m (intercalate ", " $ map (printf "%8.6f") l)

    printDLine :: String -> String -> String -> [Double] -> IO ()
    printDLine mf m vf v = do
      void $ printf mf m
      mapM_ (printf vf) v
      putStrLn ""