packages feed

hquantlib-time-0.1.1: src/QuantLib/Time/DayCounter.hs

module QuantLib.Time.DayCounter
  ( module QuantLib.Time.DayCounter,
  )
where

import Data.Time.Calendar (Day (..), toGregorian)
import QuantLib.Time.Date (Date)

-- | Day counter type class
class DayCounter m where
  -- | Name of day counter
  dcName :: m -> String

  -- | Number of business days inbetween
  dcCount :: m -> Date -> Date -> Int

  -- | Year fraction
  dcYearFraction :: m -> Date -> Date -> Double

{-
data SimpleDayCounter = SimpleDayCounter

instance DayCounter SimpleDayCounter where
        dcName _        = "Simple"
        dcCount         = undefined
        dcYearFraction  = undefined
-}

-- | Thirty day counters as in QuantLib
data Thirty360 = ThirtyUSA | ThirtyEuropean | ThirtyItalian

instance DayCounter Thirty360 where
  dcName ThirtyUSA = "Thirty USA"
  dcName ThirtyEuropean = "Thirty Euro"
  dcName ThirtyItalian = "Thirty Italian"

  dcYearFraction dc fromDate toDate = fromIntegral (dcCount dc fromDate toDate) / 360.0

  dcCount ThirtyUSA fd td = 360 * (yy2 - yy1) + 30 * (mm2 - mm1 -1) + max 0 (30 - dd1) + min 30 dd2
    where
      (yy1, mm1, dd1) = intGregorian fd
      (yy2, m2, d2) = intGregorian td
      (dd2, mm2) = adjust dd1 d2 m2
      adjust x1 x2 z2
        | x2 == 31 && x1 < 30 = (1, z2 + 1)
        | otherwise = (x2, z2)
  dcCount ThirtyEuropean fd td = 360 * (yy2 - yy1) + 30 * (m2 - m1 -1) + max 0 (30 - d1) + min 30 d2
    where
      (yy1, m1, d1) = intGregorian fd
      (yy2, m2, d2) = intGregorian td
  dcCount ThirtyItalian fd td = 360 * (yy2 - yy1) + 30 * (mm2 - mm1 -1) + max 0 (30 - dd1) + min 30 dd2
    where
      (yy1, mm1, d1) = intGregorian fd
      (yy2, mm2, d2) = intGregorian td
      dd1 = adjust d1 mm1
      dd2 = adjust d2 mm2
      adjust x1 z1
        | z1 == 2 && x1 > 27 = 30
        | otherwise = x1

intGregorian :: Day -> (Int, Int, Int)
intGregorian date = (fromIntegral y, m, d)
  where
    (y, m, d) = toGregorian date