packages feed

hasquant-0.7.0.0: QuantLib/Time/Schedule.chs

-- DayCounterConstructor is declared in QuantLib.Internal.CalendarEnum, but its Read instance
-- needs `calendar` (QuantLib.Time.Calendar), and CalendarEnum -> Schedule -> CalendarEnum
-- would be a cycle if the instance lived there instead -- see deriveReadPlain's comment in
-- Internal/Syntax.hs. Deliberate, not a stray orphan.
{-# OPTIONS_GHC -Wno-orphans #-}
module QuantLib.Time.Schedule
  (
    -- * Types
    DayCounter
  , Schedule
  , DayCounterConstructor(..)
  , DateGenerationRule(..)
  , TimeUnit(..)
  , Frequency(..)

    -- * Constructors
  , dayCounter
  , schedule
  , fromDates

    -- * Inspectors
    -- ** Day counts
  , days
  , yearFraction
    -- ** Schedule and period operations
  , until
  , fromFrequency
  , toFrequency
  , parse
  , add
  , divide
  , lessThan
  , normalize

    -- ** Schedule fields
  , dates
  ) where
import Prelude hiding(until)

import QuantLib.Time.Date
import QuantLib.Internal
import QuantLib.Internal.Type
import QuantLib.Internal.Common
import QuantLib.Internal.CalendarEnum
import QuantLib.Time.Calendar (calendar)
import System.IO.Unsafe(unsafePerformIO)

#include "qlTypesC2HS.h"
#include "qlEnumC2HS.h"
#include "qlEnumObjects.h"

#include "ql.h"

{#enum DateGenerationRule{} deriving(Show, Eq, Read)#}
{#enum Frequency{} deriving(Show, Eq, Read, Bounded)#}

{#pointer *DayCounter foreign -> CDayCounter nocode#}
{#pointer *Schedule foreign -> CSchedule nocode#}
{#pointer *Calendar foreign -> CCalendar nocode#}

-- |Constructs a day counter of the given type and (where applicable) convention.
{#fun qlDayCounter{`Int',`Int',preErrorCheck-`String'errorCheck*-}->`DayCounter'peekDayCounter*#}

-- |Business/252 day count convention, counting business days per the given calendar.
{#fun qlDayCounterBusiness252{withCalendar*`Calendar',preErrorCheck-`String'errorCheck*-}->`DayCounter'peekDayCounter*#}

-- |Actual/Actual (Bond) day counter, using the given schedule's reference periods.
{#fun qlDayCounterActualActualBond as actualActualBond'{withSchedule*`Schedule',preErrorCheck-`String'errorCheck*-}->`DayCounter'peekDayCounter*#}

-- |Actual/Actual (ISMA) day counter, using the given schedule's reference periods.
{#fun qlDayCounterActualActualISMA as actualActualISMA'{withSchedule*`Schedule',preErrorCheck-`String'errorCheck*-}->`DayCounter'peekDayCounter*#}

dayCounter :: DayCounterConstructor -> IO DayCounter
dayCounter (Business252 x) = qlDayCounterBusiness252 x
dayCounter (ActualActualBond' sched) = actualActualBond' sched
dayCounter (ActualActualISMA' sched) = actualActualISMA' sched
dayCounter x = uncurry qlDayCounter $ mapDayCounter x

-- |Returns the number of days between two dates.
{#fun qlDayCounterDayCount as days{withDayCounter*`DayCounter',withDay*`Day',withDay*`Day'}->`Int'#}

-- |Returns the period between two dates as a fraction of year.
{#fun qlDayCounterYearFraction as yearFraction{withDayCounter*`DayCounter',withDay*`Day',withDay*`Day'
  ,withMaybeDay*`Maybe Day' -- ^refPeriodStart
  ,withMaybeDay*`Maybe Day' -- ^refPeriodEnd
  ,preErrorCheck-`String'errorCheck*-}->`Double'#}

-- |Builds a payment schedule by generating dates between effective and termination dates according to the given tenor and rule.
{#fun qlSchedule as schedule{withMaybeDay*`Maybe Day' -- ^effectiveDate
  ,withDay*`Day' -- ^terminationDate
  ,fromEnumQuantity`(Word,TimeUnit)'& -- ^tenor
  ,withCalendar*`Calendar' -- ^calendar
  ,fromEnumC`BusinessDayConvention' -- ^convention
  ,fromEnumC`BusinessDayConvention' -- ^terminationDateConvention
  ,`DateGenerationRule' -- ^rule
  ,`Bool' -- ^endOfMonth
  ,withMaybeDay*`Maybe Day' -- ^firstDate
  ,withMaybeDay*`Maybe Day' -- ^nextToLastDate
  ,preErrorCheck-`String'errorCheck*-}->`Schedule'peekSchedule*#}

-- |Builds a payment schedule from an explicit list of dates, without checking them for plausibility.
{#fun qlSchedule1 as fromDates{withDayArray*`[Day]'&
  ,withCalendar*`Calendar' -- ^calendar
  ,fromEnumC`BusinessDayConvention' -- ^convention
  ,fromMaybeEnum`Maybe BusinessDayConvention' -- ^terminationDateConvention
  ,fromMaybeEnumQuantity`Maybe (Word, TimeUnit)'& -- ^tenor
  ,fromMaybeEnum`Maybe DateGenerationRule' -- ^rule
  ,fromMaybeBool`Maybe Bool' -- ^endOfMonth
  ,preErrorCheck-`String'errorCheck*-}->`Schedule'peekSchedule*#}

-- |truncated schedule
-- TODO Introduce another Schedule type with restricted interface?
-- moreover, a fixed rate bond can be constructed from a full schedule only!
{#fun qlScheduleUntil as until{withSchedule*`Schedule',withDay*`Day',preErrorCheck-`String'errorCheck*-}->`Schedule'peekSchedule*#}

-- |returns the dates for the given Schedule object
{#fun qlScheduleDates as dates{withSchedule*`Schedule',preArray-`[Day]'&peekDayArray*,preErrorCheck-`String'errorCheck*-}->`()'#}

-- |returns a Period from a given Frequency (e.g. 6M from SemiAnnual)
{#fun qlPeriodFromFrequency1 as fromFrequency{`Frequency',preEnum-`TimeUnit'peekEnum*,preErrorCheck-`String'errorCheck*-}->`Word'fromIntegral#}

-- |returns a Frequency from a given Period (e.g. SemiAnnual from 6M)
{#fun qlPeriodToFrequency1 as toFrequency{fromEnumQuantity`Word,TimeUnit'&,preErrorCheck-`String'errorCheck*-}->`Frequency'#}

-- |Parses a period from its short-format string representation (e.g. \"6M\").
{#fun qlPeriodParserParse1 as parse{`String',preEnum-`TimeUnit'peekEnum*,preErrorCheck-`String'errorCheck*-}->`Int'#}

{#fun qlPeriodAdd1 as addPeriods{fromEnumQuantity`Int,TimeUnit'&,fromEnumQuantity`Int,TimeUnit'&,preEnum-`TimeUnit'peekEnum*,preErrorCheck-`String'errorCheck*-}->`Int'#}

-- |Adds two periods together.
add :: (Int, TimeUnit) -> (Int, TimeUnit) -> IO (Int, TimeUnit)
add = addPeriods

-- |Divides a period's length by an integer divisor.
{#fun qlPeriodDivide1 as divide{fromEnumQuantity`Int,TimeUnit'&,`Int',preEnum-`TimeUnit'peekEnum*,preErrorCheck-`String'errorCheck*-}->`Int'#}

-- |Compares two periods, converting to a common time unit as needed.
{#fun qlPeriodsLT1 as lessThan{fromEnumQuantity`Int,TimeUnit'&,fromEnumQuantity`Int,TimeUnit'&,preErrorCheck-`String'errorCheck*-}->`Bool'#}

-- |Normalizes a period to the coarsest equivalent time unit (e.g. 12M to 1Y).
{#fun qlPeriodNormalize1 as normalize{fromEnumQuantity`Int,TimeUnit'&,preEnum-`TimeUnit'peekEnum*,preErrorCheck-`String'errorCheck*-}->`Int'#}

-- Same NOINLINE reasoning as QuantLib.Time.Calendar's own unsafeCalendar (which this can't
-- reuse: DayCounterConstructor's Read instance needs its own top-level NOINLINE binding, not
-- a shared one, so GHC can't float/duplicate-inline this call site independently of that
-- one's).
unsafeCalendar :: CalendarConstructor -> Calendar
unsafeCalendar = unsafePerformIO . calendar
{-# NOINLINE unsafeCalendar #-}

-- Hand-written, not `deriving`/TH-spliced: DayCounterConstructor's Business252 case carries a
-- live Calendar field (see QuantLib.Internal.Syntax.deriveReadPlain's comment for why the
-- generated part is spliced back in CalendarEnum.chs instead of here).
-- ActualActualBond'/ActualActualISMA' carry a Schedule, which has no small enum-shaped
-- readable proxy at all -- they get no alternative here, so parsing their name deliberately
-- falls through to the standard Read "no parse" failure.
instance Read DayCounterConstructor where
  readsPrec d r = readDayCounterConstructorPlain d r
    ++ readParen (d > 10) (\r' ->
         [ (Business252 c, s1)
         | ("Business252", s0) <- lex r'
         , (p, s1) <- readsPrec 11 s0, let c = unsafeCalendar p
         ]) r

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