packages feed

hasquant-0.7.0.0: QuantLib/TermStructure/Commodity.chs

module QuantLib.TermStructure.Commodity
  (
    -- * Types
    CommodityCurve
  , ExchangeContract
  , ExchangeContracts

    -- * Constructors
  , commodityCurve

    -- * Mutators
  , setBasisOfCurve

    -- * Inspectors
    -- ** Curve metadata and nodes
  , name
  , commodityType
  , unitOfMeasure
  , currency
  , nodes
  , isEmpty
  , basisOfCurve
    -- ** Prices and contracts
  , price
  , basisOfPrice
  , priceNearby
  , underlyingPriceDate
  ) where
import QuantLib.Internal
import QuantLib.Internal.Type
import Data.List.NonEmpty(NonEmpty, toList)

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

#include "ql.h"

{#pointer *Calendar foreign -> CCalendar nocode#}
{#pointer *Currency foreign -> CCurrency nocode#}
{#pointer *CommodityType foreign -> CCommodityType nocode#}
{#pointer *UnitOfMeasure foreign -> CUnitOfMeasure nocode#}
{#pointer *DayCounter foreign -> CDayCounter nocode#}
{#pointer *QlCommodityCurve as CommodityCurve foreign -> CCommodityCurve' nocode#}

-- |Construct a commodity price curve: a named, interpolated (forward-flat) price curve over a
-- fixed set of dates, for a given commodity type\/currency\/unit of measure. QuantLib's no-dates
-- constructor populated later via @setPrices@ is not bound, so this is the only way to build one.
commodityCurve :: String -> CommodityType -> Currency -> UnitOfMeasure -> Calendar
  -> NonEmpty (Day, Double) -> DayCounter -> IO CommodityCurve
commodityCurve curveName ct ccy uom cal curveNodes dc =
  qlCommodityCurve curveName ct ccy uom cal dates prices dc
  where (dates, prices) = unzip (toList curveNodes)

{#fun qlCommodityCurve
  {`String' -- ^name
  ,withCommodityType*`CommodityType'
  ,withCurrency*`Currency'
  ,withUnitOfMeasure*`UnitOfMeasure'
  ,withCalendar*`Calendar'
  ,withDayArray*`[Day]'&
  ,withDoubleArray*`[Double]'&
  ,withDayCounter*`DayCounter'
  ,preErrorCheck-`String'errorCheck*-}->`CommodityCurve'peekCommodityCurve*#}

-- |The curve's name, as given at construction.
{#fun qlCommodityCurveName as name{withGenTermStructure*`CommodityCurve'}->`String'peekDynString*#}

-- |The commodity type this curve prices.
{#fun qlCommodityCurveCommodityType as commodityType{withGenTermStructure*`CommodityCurve',preErrorCheck-`String'errorCheck*-}->`CommodityType'peekCommodityType*#}

-- |The unit of measure this curve's prices are quoted in.
{#fun qlCommodityCurveUnitOfMeasure as unitOfMeasure{withGenTermStructure*`CommodityCurve',preErrorCheck-`String'errorCheck*-}->`UnitOfMeasure'peekUnitOfMeasure*#}

-- |The currency this curve's prices are quoted in.
{#fun qlCommodityCurveCurrency as currency{withGenTermStructure*`CommodityCurve',preErrorCheck-`String'errorCheck*-}->`Currency'peekCurrency*#}

-- |The curve's nodes, as @(date, price)@ pairs in construction order.
nodes :: CommodityCurve -> IO [(Day, Double)]
nodes curve = do
  dates <- qlCommodityCurveDates curve
  prices <- qlCommodityCurvePrices curve
  pure (zip dates prices)
{#fun qlCommodityCurveDates as qlCommodityCurveDates{withGenTermStructure*`CommodityCurve',preArray-`[Day]'&peekDayArray*,preErrorCheck-`String'errorCheck*-}->`()'#}
{#fun qlCommodityCurvePrices as qlCommodityCurvePrices{withGenTermStructure*`CommodityCurve',preArray-`[Double]'&peekDoubleArray*,preErrorCheck-`String'errorCheck*-}->`()'#}

-- |Whether this curve has any nodes.
{#fun pure qlCommodityCurveEmpty as isEmpty{withGenTermStructure*`CommodityCurve'}->`Bool'#}

-- |The basis curve this curve was chained to via 'setBasisOfCurve', if any.
{#fun qlCommodityCurveBasisOfCurve as basisOfCurve{withGenTermStructure*`CommodityCurve'}->`Maybe CommodityCurve'peekMaybeCommodityCurve*#}

-- |Chain this curve to a basis curve: prices returned by 'price'\/'basisOfPrice'
-- then include the basis curve's price on top of this curve's own.
{#fun qlCommodityCurveSetBasisOfCurve as setBasisOfCurve{withGenTermStructure*`CommodityCurve',withGenTermStructure*`CommodityCurve',preErrorCheck-`String'errorCheck*-}->`()'#}

-- |A dated exchange contract: a code, its expiration date, and the start/end dates of the
-- underlying delivery period it corresponds to. A plain tuple, per the @Money@/'Quantity'-as-tuple
-- convention -- it carries no calculation of its own upstream, only three inspectors that would
-- just be tuple projections.
type ExchangeContract = (String, Day, Day, Day) -- ^code, expirationDate, underlyingStartDate, underlyingEndDate

-- |QuantLib's @std::map\<Date,ExchangeContract\>@: a set of exchange contracts, keyed by the date
-- 'priceNearby'\/'underlyingPriceDate' roll onto (upstream's own
-- @lower_bound@ walk finds the first key at or after the query date, then steps @nearbyOffset - 1@
-- further). Marshalled as an association list, not an actual 'Data.Map.Map' -- the C shim rebuilds
-- the real @std::map@ itself so key order doesn't need to be pre-sorted on the Haskell side.
type ExchangeContracts = [(Day, ExchangeContract)]

-- |Split an 'ExchangeContracts' into the five parallel lists the low-level bindings below take.
-- Each component uses its own flat marshaller because c2hs's @&@ tuple splitter consumes only
-- two C arguments.
splitExchangeContracts :: ExchangeContracts -> ([Day], [String], [Day], [Day], [Day])
splitExchangeContracts ecs =
  ( map fst ecs
  , [c | (_, (c, _, _, _)) <- ecs]
  , [x | (_, (_, x, _, _)) <- ecs]
  , [s | (_, (_, _, s, _)) <- ecs]
  , [e | (_, (_, _, _, e)) <- ecs] )

{#fun qlCommodityCurvePrice as qlCommodityCurvePrice_
  {withGenTermStructure*`CommodityCurve'
  ,withDay*`Day'
  ,withDayArray*`[Day]'&
  ,withStringArray*`[String]'&
  ,withDayArray*`[Day]'&
  ,withDayArray*`[Day]'&
  ,withDayArray*`[Day]'&
  ,`Int' -- ^nearbyOffset
  ,preErrorCheck-`String'errorCheck*-}->`Double'#}

-- |The curve's price for a date, plus any chained basis curve's price, rolling forward onto
-- nearby exchange contracts when @nearbyOffset > 0@ (upstream's own @price@ never touches
-- @exchangeContracts@ otherwise). 'price' is this with no exchange contracts and
-- offset @0@, which reproduces the flat (no-rolling) case exactly.
priceNearby :: CommodityCurve -> Day -> ExchangeContracts -> Int -> IO Double
priceNearby curve date ecs nearbyOffset =
  qlCommodityCurvePrice_ curve date keys codes expirations starts ends nearbyOffset
  where (keys, codes, expirations, starts, ends) = splitExchangeContracts ecs

-- |The curve's price for a date, plus any chained basis curve's price. This is
-- 'priceNearby' with no exchange contracts and offset @0@ -- the flat (no
-- nearby-rolling) case, which never touches @exchangeContracts@ upstream either way.
price :: CommodityCurve -> Day -> IO Double
price curve date = priceNearby curve date [] 0

-- |The chained basis curve's price alone (excluding this curve's own price), for a date.
{#fun qlCommodityCurveBasisOfPrice as basisOfPrice{withGenTermStructure*`CommodityCurve',withDay*`Day',preErrorCheck-`String'errorCheck*-}->`Double'#}

{#fun qlCommodityCurveUnderlyingPriceDate as qlCommodityCurveUnderlyingPriceDate_
  {withGenTermStructure*`CommodityCurve'
  ,withDay*`Day'
  ,withDayArray*`[Day]'&
  ,withStringArray*`[String]'&
  ,withDayArray*`[Day]'&
  ,withDayArray*`[Day]'&
  ,withDayArray*`[Day]'&
  ,`Int' -- ^nearbyOffset
  ,preErrorCheck-`String'errorCheck*-}->`Day'toDay#}

-- |The date whose price a nearby roll (@nearbyOffset > 0@) actually reads: the underlying
-- contract's start date at the @nearbyOffset@\'th exchange contract at or after @date@. Throws if
-- @nearbyOffset <= 0@, or if fewer than @nearbyOffset@ contracts are available from @date@ onward.
underlyingPriceDate :: CommodityCurve -> Day -> ExchangeContracts -> Int -> IO Day
underlyingPriceDate curve date ecs nearbyOffset =
  qlCommodityCurveUnderlyingPriceDate_ curve date keys codes expirations starts ends nearbyOffset
  where (keys, codes, expirations, starts, ends) = splitExchangeContracts ecs

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