hasquant-0.6.0.0: QuantLib/Commodity.chs
module QuantLib.Commodity
(
CommodityType
, commodityType
, nullCommodityType
, commodityTypeCode
, commodityTypeName
, commodityTypeEmpty
, UnitOfMeasure
, UnitOfMeasureType(..)
, unitOfMeasure
, unitOfMeasureName
, unitOfMeasureCode
, unitOfMeasureType
, unitOfMeasureEmpty
, lotUnitOfMeasure
, barrelUnitOfMeasure
, mtUnitOfMeasure
, mbUnitOfMeasure
, gallonUnitOfMeasure
, litreUnitOfMeasure
, kilolitreUnitOfMeasure
, tokyoKilolitreUnitOfMeasure
, PaymentTerm
, PaymentTermEventType(..)
, paymentTerm
, paymentTermName
, paymentTermEventType
, paymentTermOffsetDays
, paymentTermCalendar
, paymentTermEmpty
, paymentTermGetPaymentDate
, Quantity
, roundedQuantity
, closeQuantity
, closeEnoughQuantity
, CommodityUnitCost
, DateInterval
, isDateBetween
, intersection
, PricingPeriod
, pricingPeriodStartDate
, pricingPeriodEndDate
, pricingPeriodPaymentDate
, pricingPeriodQuantity
, PricingPeriods
, pricingPeriod
, UnitOfMeasureConversion
, UnitOfMeasureConversionType(..)
, unitOfMeasureConversion
, unitOfMeasureConversionSource
, unitOfMeasureConversionTarget
, unitOfMeasureConversionCommodityType
, unitOfMeasureConversionType
, unitOfMeasureConversionFactor
, unitOfMeasureConversionCode
, convertQuantity
, chainUnitOfMeasureConversion
, lookupUomConversion
, addUomConversion
, clearUomConversions
, commoditySettingsCurrency
, setCommoditySettingsCurrency
, commoditySettingsUnitOfMeasure
, setCommoditySettingsUnitOfMeasure
) where
import QuantLib.Internal
import QuantLib.Internal.Type
import QuantLib.Internal.Common
import Foreign.Marshal.Alloc(alloca)
#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 *PaymentTerm foreign -> CPaymentTerm nocode#}
{#pointer *UnitOfMeasureConversion foreign -> CUnitOfMeasureConversion nocode#}
{#enum UnitOfMeasureConversionType{} deriving(Show, Eq)#}
-- |Construct a custom commodity type identified by its code (e.g. \"HO\") and descriptive name
-- (e.g. \"Heating Oil\"). QuantLib has no fixed enum of commodity types -- every instance is
-- user-registered by code, the same way a custom 'QuantLib.Currency.currency'' is.
{#fun qlCommodityType as commodityType{`String' -- ^code
,`String' -- ^name
,preErrorCheck-`String'errorCheck*-}->`CommodityType'peekCommodityType*#}
-- |The fixed placeholder commodity type QuantLib itself uses where no real commodity type
-- applies (e.g. in unit-of-measure-only conversions).
{#fun qlNullCommodityType as nullCommodityType{preErrorCheck-`String'errorCheck*-}->`CommodityType'peekCommodityType*#}
-- |The commodity code, e.g. \"HO\".
{#fun pure qlCommodityTypeCode as commodityTypeCode{withCommodityType*`CommodityType'}->`String'peekDynString*#}
-- |The descriptive name, e.g. \"Heating Oil\".
{#fun pure qlCommodityTypeName as commodityTypeName{withCommodityType*`CommodityType'}->`String'peekDynString*#}
-- |Whether this is a usable instance (as opposed to one built via a default constructor).
{#fun pure qlCommodityTypeEmpty as commodityTypeEmpty{withCommodityType*`CommodityType'}->`Bool'#}
-- |Construct a custom unit of measure from its descriptive name (e.g. \"Barrels\"), code (e.g.
-- \"BBL\"), and type (mass/volume/energy/quantity).
{#fun qlUnitOfMeasure as unitOfMeasure{`String' -- ^name
,`String' -- ^code
,fromEnumC`UnitOfMeasureType'
,preErrorCheck-`String'errorCheck*-}->`UnitOfMeasure'peekUnitOfMeasure*#}
-- |The descriptive name, e.g. \"Barrels\".
{#fun pure qlUnitOfMeasureName as unitOfMeasureName{withUnitOfMeasure*`UnitOfMeasure'}->`String'peekDynString*#}
-- |The code, e.g. \"BBL\", \"MT\".
{#fun pure qlUnitOfMeasureCode as unitOfMeasureCode{withUnitOfMeasure*`UnitOfMeasure'}->`String'peekDynString*#}
-- |The unit's type (mass/volume/energy/quantity).
{#fun pure qlUnitOfMeasureUnitType as unitOfMeasureType{withUnitOfMeasure*`UnitOfMeasure'}->`UnitOfMeasureType'toEnumC#}
-- |Whether this is a usable instance (as opposed to one built via a default constructor).
{#fun pure qlUnitOfMeasureEmpty as unitOfMeasureEmpty{withUnitOfMeasure*`UnitOfMeasure'}->`Bool'#}
-- |The lot, a dimensionless 'UnitOfMeasure::Quantity' unit.
{#fun qlLotUnitOfMeasure as lotUnitOfMeasure{preErrorCheck-`String'errorCheck*-}->`UnitOfMeasure'peekUnitOfMeasure*#}
-- |Barrels (BBL), the base petroleum volume unit.
{#fun qlBarrelUnitOfMeasure as barrelUnitOfMeasure{preErrorCheck-`String'errorCheck*-}->`UnitOfMeasure'peekUnitOfMeasure*#}
-- |Metric tonnes (MT).
{#fun qlMTUnitOfMeasure as mtUnitOfMeasure{preErrorCheck-`String'errorCheck*-}->`UnitOfMeasure'peekUnitOfMeasure*#}
-- |Thousand barrels (MB).
{#fun qlMBUnitOfMeasure as mbUnitOfMeasure{preErrorCheck-`String'errorCheck*-}->`UnitOfMeasure'peekUnitOfMeasure*#}
-- |US gallons.
{#fun qlGallonUnitOfMeasure as gallonUnitOfMeasure{preErrorCheck-`String'errorCheck*-}->`UnitOfMeasure'peekUnitOfMeasure*#}
-- |Litres.
{#fun qlLitreUnitOfMeasure as litreUnitOfMeasure{preErrorCheck-`String'errorCheck*-}->`UnitOfMeasure'peekUnitOfMeasure*#}
-- |Kilolitres.
{#fun qlKilolitreUnitOfMeasure as kilolitreUnitOfMeasure{preErrorCheck-`String'errorCheck*-}->`UnitOfMeasure'peekUnitOfMeasure*#}
-- |Tokyo kilolitres.
{#fun qlTokyoKilolitreUnitOfMeasure as tokyoKilolitreUnitOfMeasure{preErrorCheck-`String'errorCheck*-}->`UnitOfMeasure'peekUnitOfMeasure*#}
-- |Construct a payment term: a named offset (in calendar-adjusted days) from either the trade
-- date or the pricing-period end date.
{#fun qlPaymentTerm as paymentTerm{`String' -- ^name
,fromEnumC`PaymentTermEventType'
,`Int' -- ^offsetDays
,withCalendar*`Calendar'
,preErrorCheck-`String'errorCheck*-}->`PaymentTerm'peekPaymentTerm*#}
-- |The payment term's name, e.g. \"Pricing end + 5 days\".
{#fun pure qlPaymentTermName as paymentTermName{withPaymentTerm*`PaymentTerm'}->`String'peekDynString*#}
-- |Whether the offset is measured from the trade date or the pricing date.
{#fun pure qlPaymentTermEventType_ as paymentTermEventType{withPaymentTerm*`PaymentTerm'}->`PaymentTermEventType'toEnumC#}
-- |The number of (calendar-adjusted) offset days.
{#fun pure qlPaymentTermOffsetDays as paymentTermOffsetDays{withPaymentTerm*`PaymentTerm'}->`Int'#}
-- |The calendar used to adjust the payment date.
{#fun qlPaymentTermCalendar as paymentTermCalendar{withPaymentTerm*`PaymentTerm',preErrorCheck-`String'errorCheck*-}->`Calendar'peekCalendar*#}
-- |Whether this is a usable instance (as opposed to one built via a default constructor).
{#fun pure qlPaymentTermEmpty as paymentTermEmpty{withPaymentTerm*`PaymentTerm'}->`Bool'#}
-- |Applies the term's offset (and calendar adjustment) to a trade or pricing-period-end date to
-- get the actual payment date.
{#fun qlPaymentTermGetPaymentDate as paymentTermGetPaymentDate
{withPaymentTerm*`PaymentTerm',withDay*`Day',preErrorCheck-`String'errorCheck*-}->`Day'toDay#}
-- |An amount of a commodity: a 'CommodityType', a 'UnitOfMeasure', and a plain amount. Marshalled
-- as a flat triple rather than a wrapper type (per the @Money@-as-tuple convention) -- and, unlike
-- 'CommodityUnitCost' below, its three inspectors are just tuple projections, so they need no
-- binding at all. c2hs's @&@ tuple-splitter only ever consumes two C arguments (confirmed against
-- its source, not just by trial), so every function below that takes or returns a 'Quantity'
-- marshals it as three flat 'CommodityType'\/'UnitOfMeasure'\/'Double' arguments instead of one
-- combined tuple.
type Quantity = (CommodityType, UnitOfMeasure, Double)
{#fun pure qlQuantityRoundedAmount as quantityRoundedAmount{withUnitOfMeasure*`UnitOfMeasure',`Double'}->`Double'#}
-- |Round a quantity's amount per its unit of measure's rounding convention. The commodity type and
-- unit of measure are unaffected by rounding, so they're carried straight through in Haskell
-- rather than round-tripped through the C++ call.
roundedQuantity :: Quantity -> Quantity
roundedQuantity (ct, uom, amount) = (ct, uom, quantityRoundedAmount uom amount)
{#fun qlQuantityClose as qlQuantityClose_
{withCommodityType*`CommodityType',withUnitOfMeasure*`UnitOfMeasure',`Double'
,withCommodityType*`CommodityType',withUnitOfMeasure*`UnitOfMeasure',`Double'
,`Int',preErrorCheck-`String'errorCheck*-}->`Bool'#}
-- |Whether two quantities are close to within @n@ ULPs (default 42 upstream), after converting
-- the second to the first's unit of measure if their units differ (which throws unless a
-- conversion is reachable -- see 'lookupUomConversion').
closeQuantity :: Quantity -> Quantity -> Int -> IO Bool
closeQuantity (ct1, uom1, amt1) (ct2, uom2, amt2) n = qlQuantityClose_ ct1 uom1 amt1 ct2 uom2 amt2 n
{#fun qlQuantityCloseEnough as qlQuantityCloseEnough_
{withCommodityType*`CommodityType',withUnitOfMeasure*`UnitOfMeasure',`Double'
,withCommodityType*`CommodityType',withUnitOfMeasure*`UnitOfMeasure',`Double'
,`Int',preErrorCheck-`String'errorCheck*-}->`Bool'#}
-- |As 'closeQuantity', but using QuantLib's relative (rather than absolute) closeness test.
closeEnoughQuantity :: Quantity -> Quantity -> Int -> IO Bool
closeEnoughQuantity (ct1, uom1, amt1) (ct2, uom2, amt2) n = qlQuantityCloseEnough_ ct1 uom1 amt1 ct2 uom2 amt2 n
-- |A commodity's unit cost: a cash amount (a @(Double, Currency)@ pair, standing in for
-- QuantLib's @Money@) per 'UnitOfMeasure'. A plain tuple, like 'Quantity' -- it carries no
-- calculation of its own upstream.
type CommodityUnitCost = (Double, Currency, UnitOfMeasure)
-- |A date range, inclusive of both ends. No C++ call is needed for either operation below --
-- 'Day' (from @Data.Time.Calendar@) already has the 'Ord' instance QuantLib's own comparisons rely
-- on, so both are plain Haskell functions rather than shim calls.
type DateInterval = (Day, Day)
-- |Whether a date falls within the interval, optionally excluding either end.
isDateBetween :: DateInterval
-> Day -- ^date
-> Bool -- ^includeFirst
-> Bool -- ^includeLast
-> Bool
isDateBetween (startDate, endDate) date includeFirst includeLast =
(if includeFirst then date >= startDate else date > startDate) &&
(if includeLast then date <= endDate else date < endDate)
-- |The overlap of two date intervals, or 'Nothing' if they don't overlap at all (upstream returns
-- a default-constructed, both-dates-null 'DateInterval' in this case; a 'Maybe' is the natural
-- Haskell equivalent).
intersection :: DateInterval -> DateInterval -> Maybe DateInterval
intersection (s1, e1) (s2, e2)
| (s1 < s2 && e1 < s2) || (s1 > e2 && e1 > e2) = Nothing
| otherwise = Just (max s1 s2, min e1 e2)
-- |A 'DateInterval' over which a fixed 'Quantity' of a commodity is priced, paid on
-- 'pricingPeriodPaymentDate'. Unlike 'Quantity'\/'CommodityUnitCost', this is a real record
-- rather than a tuple, per the user's explicit choice -- it's used as a named unit across every
-- energy-swap constructor (Stage 6), where a flat tuple would be unreadable positionally.
data PricingPeriod = PricingPeriod
{ pricingPeriodStartDate :: Day
, pricingPeriodEndDate :: Day
, pricingPeriodPaymentDate :: Day
, pricingPeriodQuantity :: Quantity
} deriving (Show, Eq)
type PricingPeriods = [PricingPeriod]
-- |Construct a 'PricingPeriod', enforcing upstream's @DateInterval@ invariant that the end date
-- is not before the start date.
pricingPeriod :: Day -> Day -> Day -> Quantity -> PricingPeriod
pricingPeriod startDate endDate
| endDate < startDate = error "pricingPeriod: end date must be >= start date"
| otherwise = PricingPeriod startDate endDate
-- |Construct a conversion factor between two units of measure for a given commodity type: a unit
-- of @source@ is worth @conversionFactor@ units of @target@.
{#fun qlUnitOfMeasureConversion as unitOfMeasureConversion
{withCommodityType*`CommodityType'
,withUnitOfMeasure*`UnitOfMeasure' -- ^source
,withUnitOfMeasure*`UnitOfMeasure' -- ^target
,`Double' -- ^conversionFactor
,preErrorCheck-`String'errorCheck*-}->`UnitOfMeasureConversion'peekUnitOfMeasureConversion*#}
-- |The source unit of measure.
{#fun qlUnitOfMeasureConversionSource as unitOfMeasureConversionSource{withUnitOfMeasureConversion*`UnitOfMeasureConversion',preErrorCheck-`String'errorCheck*-}->`UnitOfMeasure'peekUnitOfMeasure*#}
-- |The target unit of measure.
{#fun qlUnitOfMeasureConversionTarget as unitOfMeasureConversionTarget{withUnitOfMeasureConversion*`UnitOfMeasureConversion',preErrorCheck-`String'errorCheck*-}->`UnitOfMeasure'peekUnitOfMeasure*#}
-- |The commodity type this conversion applies to.
{#fun qlUnitOfMeasureConversionCommodityType as unitOfMeasureConversionCommodityType{withUnitOfMeasureConversion*`UnitOfMeasureConversion',preErrorCheck-`String'errorCheck*-}->`CommodityType'peekCommodityType*#}
-- |Whether the conversion was given directly, or derived by chaining two other conversions.
{#fun pure qlUnitOfMeasureConversionType_ as unitOfMeasureConversionType{withUnitOfMeasureConversion*`UnitOfMeasureConversion'}->`UnitOfMeasureConversionType'#}
-- |The conversion factor: a unit of the source is worth this many units of the target.
{#fun pure qlUnitOfMeasureConversionFactor as unitOfMeasureConversionFactor{withUnitOfMeasureConversion*`UnitOfMeasureConversion'}->`Double'#}
-- |A code identifying the conversion, e.g. \"Heating OilMTBBL\".
{#fun pure qlUnitOfMeasureConversionCode as unitOfMeasureConversionCode{withUnitOfMeasureConversion*`UnitOfMeasureConversion'}->`String'peekDynString*#}
{#fun qlUnitOfMeasureConversionConvert as qlUnitOfMeasureConversionConvert_
{withUnitOfMeasureConversion*`UnitOfMeasureConversion'
,withCommodityType*`CommodityType',withUnitOfMeasure*`UnitOfMeasure',`Double'
,alloca-`CommodityType'peekCommodityTypePtr*
,alloca-`UnitOfMeasure'peekUnitOfMeasurePtr*
,preErrorCheck-`String'errorCheck*-}->`Double'#}
-- |Apply the conversion factor to a quantity, converting it from the conversion's source to its
-- target unit of measure (or vice versa). Throws if the quantity's unit of measure is on neither
-- side of the conversion.
convertQuantity :: UnitOfMeasureConversion -> Quantity -> IO Quantity
convertQuantity conv (ct, uom, amount) = do
(amount', ct', uom') <- qlUnitOfMeasureConversionConvert_ conv ct uom amount
pure (ct', uom', amount')
-- |Combine two conversions sharing a common unit of measure into a derived conversion between
-- their other two units. Throws if the conversions don't share a common unit.
{#fun qlUnitOfMeasureConversionChain as chainUnitOfMeasureConversion
{withUnitOfMeasureConversion*`UnitOfMeasureConversion'
,withUnitOfMeasureConversion*`UnitOfMeasureConversion'
,preErrorCheck-`String'errorCheck*-}->`UnitOfMeasureConversion'peekUnitOfMeasureConversion*#}
-- |Look up a (possibly derived, via triangulation) conversion between two units of measure for a
-- given commodity type. Throws if none can be found. Pre-populated with a set of known petroleum
-- conversion factors even before any 'addUomConversion' call.
{#fun qlUnitOfMeasureConversionManagerLookup as lookupUomConversion
{withCommodityType*`CommodityType'
,withUnitOfMeasure*`UnitOfMeasure' -- ^source
,withUnitOfMeasure*`UnitOfMeasure' -- ^target
,`UnitOfMeasureConversionType'
,preErrorCheck-`String'errorCheck*-}->`UnitOfMeasureConversion'peekUnitOfMeasureConversion*#}
-- |Register a conversion with the global unit-of-measure conversion repository, replacing any
-- existing conversion between the same commodity type and pair of units.
{#fun qlUnitOfMeasureConversionManagerAdd as addUomConversion{withUnitOfMeasureConversion*`UnitOfMeasureConversion'}->`()'#}
-- |Reset the unit-of-measure conversion repository back to its built-in set of known petroleum
-- conversion factors, discarding anything added via 'addUomConversion'.
{#fun qlUnitOfMeasureConversionManagerClear as clearUomConversions{}->`()'#}
-- |The global commodity currency setting (defaults to USD).
{#fun qlCommoditySettingsCurrency as commoditySettingsCurrency{preErrorCheck-`String'errorCheck*-}->`Currency'peekCurrency*#}
-- |Set the global commodity currency setting.
{#fun qlCommoditySettingsSetCurrency as setCommoditySettingsCurrency{withCurrency*`Currency'}->`()'#}
-- |The global commodity unit-of-measure setting (defaults to barrels).
{#fun qlCommoditySettingsUnitOfMeasure as commoditySettingsUnitOfMeasure{preErrorCheck-`String'errorCheck*-}->`UnitOfMeasure'peekUnitOfMeasure*#}
-- |Set the global commodity unit-of-measure setting.
{#fun qlCommoditySettingsSetUnitOfMeasure as setCommoditySettingsUnitOfMeasure{withUnitOfMeasure*`UnitOfMeasure'}->`()'#}
-- vim: set ff=unix ts=8 sts=2 sw=2 et: