packages feed

HQu-0.0.0.0: src/Q/Time/DayCounter.hs

{-# LANGUAGE DeriveGeneric        #-}
{-# LANGUAGE FlexibleInstances    #-}
{-# LANGUAGE UndecidableInstances #-}
module Q.Time.DayCounter (
  DayCounter(..),
  Thirty360(..)
  ) where

import           Data.Time.Calendar
import           GHC.Generics
-- |Day counter type class
class DayCounter m where
  dcName         :: m -> String -- ^Name of the day counter.
  dcCount        :: m -> Day -> Day -> Int -- ^Number of business days inbetween
  dcYearFraction :: m -> Day -> Day -> Double -- ^Year fraction between 2 dates.


-- | Thirty day counters as in QuantLib
data Thirty360 = ThirtyUSA | ThirtyEuropean | ThirtyItalian
  deriving (Generic, Eq, Show, Read)

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