hasquant-0.7.0.0: QuantLib/Model.chs
{-# LANGUAGE FlexibleInstances #-}
-- |Historical volatility estimators bound here (@ql\/models\/volatility@, @ql\/prices.hpp@) --
-- 'Garch11', GarmanKlass\/Parkinson, 'ConstantEstimator', 'SimpleLocalEstimator' -- are distinct
-- from "QuantLib.TermStructure.Volatility", which covers forward-looking implied-volatility
-- surfaces and smiles. None of these classes have any calculations of their own beyond a single
-- @calculate@ call and are never needed as an argument type elsewhere, so only 'Garch11' --
-- whose calibrated state ('alpha' etc.) is queried repeatedly -- gets a dedicated Haskell type;
-- every GarmanKlass variant, 'ConstantEstimator', and 'SimpleLocalEstimator' are bound as a
-- single construct-and-calculate function each.
module QuantLib.Model
(
-- * Types
-- ** Model hierarchy
GenCalibratedModel
, CalibratedModel
, GenShortRateModel
, ShortRateModel
, GenOneFactorAffineModel
, OneFactorAffineModel
, HullWhite
, G2
, GenHestonModel
, HestonModel
, PiecewiseTimeDependentHestonModel
, GenBatesModel
, BatesModel
, BatesDetJumpModel
, GenBatesDoubleExpModel
, BatesDoubleExpModel
, BatesDoubleExpDetJumpModel
, GJRGARCHModel
, Gaussian1dModel
, Gsr
, MarkovFunctional
, LiborForwardModel
, HestonSLVMCModel
, HestonSLVFDMModel
, AffineModel
, ShortRateDynamics
-- ** Historical volatility estimators
, Garch11
, Garch11Mode(..)
-- ** Calibration helpers
, GenCalibrationHelper
, CalibrationHelper
, GenBlackCalibrationHelper
, BlackCalibrationHelper
, SwaptionHelper
, BrownianGeneratorFactory
-- ** Configuration
, CalibrationErrorType(..)
, SobolBrownianOrdering(..)
, HestonSLVGreensAlgorithm(..)
, HestonSLVVarianceTransformation(..)
, HestonSLVFokkerPlanckFdmParams(..)
, HestonSLVFDMLogEntry(..)
, LfmHullWhiteParameterization
, LmCorrelationModel(..)
, LmVolatilityModel(..)
, SwaptionSpan(..)
-- * Constructors
-- ** Hierarchy and secondary-interface conversion
, AsAffineModel(..)
, AsGaussian1dModel(..)
, asCalibratedModel
, asHestonModel
, asShortRateModel
, asOneFactorAffineModel
, asBatesModel
, asBatesDoubleExpModel
, asCalibrationHelper
, asBlackCalibrationHelper
-- ** Model construction
, batesModel
, blackKarasinski
, coxIngersollRoss
, extendedCoxIngersollRoss
, g2
, generalizedHullWhite
, gjrGarchModel
, hestonModel
, mtBrownianGeneratorFactory
, sobolBrownianGeneratorFactory
, hestonSlvMcModel
, hestonSlvFdmModel
, hullWhite
, varianceGammaModel
, vasicek
, liborForwardModel
, lfmHullWhiteParameterization
, gsr
, markovFunctional
, markovFunctionalCaplet
, garch11
, garch11Calibrated
-- ** Calibration helpers
, capHelper
, hestonModelHelper
, swaptionHelper
-- * Mutators
, setCovarParam
, calibrate
, calibrateVolatilitiesIterative
, moveVolatility
, moveReversion
, setPricingEngine
-- * Inspectors
-- ** Pricing and rate calculations
, discount
, discountBond
, discountBondOption
, convexityBias
, numeraire
, gaussian1dZerobond
, gaussian1dZerobondOption
, gaussian1dForwardRate
, gaussian1dSwapRate
, gaussian1dSwapAnnuity
, gaussian1dYGrid
, lfmHullWhiteCovariance
, liborForwardModelS0
, blackPrice
, impliedVolatility
-- ** Model state and calibration
, HasLeverageFunction(..)
, HasVolatilities(..)
, HasHelperUnderlying(..)
, g2Dynamics
, shortRate
, stateProcess
, hestonSlvFdmLogEntries
, helperSwaption
, times
, fixedReversion
, params
, value
, calibrationError
, marketValue
, modelValue
, volatility
-- ** Historical volatility
, forecast
, calculate
, garmanKlassSimpleSigma
, garmanKlassSigma1
, parkinsonSigma
, garmanKlassSigma3
, garmanKlassSigma4
, garmanKlassSigma5
, garmanKlassSigma6
, constantVolatilityEstimator
, simpleLocalVolatilityEstimator
, alpha
, beta
, omega
, longTermVolatility
, logLikelihood
) where
#include "qlTypesC2HS.h"
#include "qlEnumC2HS.h"
#include "qlEnumObjects.h"
#include "ql.h"
import QuantLib.Internal
{#import QuantLib.Time.Schedule#}(Frequency)
{#import QuantLib.InterestRate#}(VolatilityType)
{#import QuantLib.CashFlow#}(RateAveragingType)
import QuantLib.Internal.Type
import QuantLib.Internal.Common
import QuantLib.TermStructure(HasHelperUnderlying(..))
import QuantLib.Math(SobolDirectionIntegers)
import Data.List(genericTake)
import Data.List.NonEmpty(NonEmpty, toList)
import Data.Maybe(fromMaybe)
{#enum CalibrationErrorType{} deriving(Show, Eq, Read)#}
-- |Sobol Brownian-bridge coordinate ordering.
data SobolBrownianOrdering = Factors | Steps | Diagonal deriving (Show, Eq, Read, Enum, Bounded)
-- |Initial-density approximation used by the Heston SLV Fokker--Planck calibrator.
data HestonSLVGreensAlgorithm = ZeroCorrelation | Gaussian | SemiAnalytical deriving (Show, Eq, Read, Enum, Bounded)
-- |Variance-coordinate transformation used by the Heston SLV Fokker--Planck calibrator.
data HestonSLVVarianceTransformation = Plain | Power | Log deriving (Show, Eq, Read, Enum, Bounded)
-- |Full numerical configuration for QuantLib's Fokker--Planck SLV calibration.
data HestonSLVFokkerPlanckFdmParams = HestonSLVFokkerPlanckFdmParams
{ hestonSlvXGrid :: !Word, hestonSlvVGrid :: !Word
, hestonSlvTMaxStepsPerYear :: !Word, hestonSlvTMinStepsPerYear :: !Word, hestonSlvTStepNumberDecay :: !Double
, hestonSlvNRannacherTimeSteps :: !Word, hestonSlvPredictionCorrectionSteps :: !Word
, hestonSlvX0Density :: !Double, hestonSlvLocalVolEpsProb :: !Double, hestonSlvMaxIntegrationIterations :: !Word
, hestonSlvVLowerEps :: !Double, hestonSlvVUpperEps :: !Double, hestonSlvVMin :: !Double
, hestonSlvV0Density :: !Double, hestonSlvVLowerBoundDensity :: !Double, hestonSlvVUpperBoundDensity :: !Double
, hestonSlvLeverageFctPropEps :: !Double, hestonSlvGreensAlgorithm :: !HestonSLVGreensAlgorithm
, hestonSlvVarianceTransformation :: !HestonSLVVarianceTransformation, hestonSlvSchemeDesc :: !FdmScheme
}
-- |A copied FDM diagnostic snapshot. Coordinates are the native mesher coordinates: the spot
-- axis is log-spot and the variance axis follows 'hestonSlvVarianceTransformation'. Density rows
-- correspond to variance coordinates and columns to log-spot coordinates.
data HestonSLVFDMLogEntry = HestonSLVFDMLogEntry
{ hestonSlvLogTime :: !Double, hestonSlvLogSpotCoordinates :: !RealVector
, hestonSlvLogVarianceCoordinates :: !RealVector, hestonSlvLogDensity :: !RealMatrix
} deriving (Show, Eq)
{#pointer *QlQuote as Quote foreign -> CQuote' nocode#}
{#pointer *QlPricingEngine as PricingEngine foreign -> CPricingEngine nocode#}
{#pointer *QlYieldTermStructure as YieldTermStructure foreign -> CYieldTermStructure' nocode#}
{#pointer *QlIborIndex as IborIndex foreign -> CIborIndex' nocode#}
{#pointer *QlOptimizationMethod as QlOptimizationMethod foreign -> COptimizationMethod nocode#}
{#pointer *QlEndCriteria as QlEndCriteria foreign -> CEndCriteria nocode#}
{#pointer *Constraint as QlConstraint foreign -> CConstraint nocode#}
{#pointer *QlLmCorrelationModel foreign -> CLmCorrelationModel nocode#}
{#pointer *QlLmVolatilityModel foreign -> CLmVolatilityModel nocode#}
{#pointer *QlLfmHullWhiteParameterization as LfmHullWhiteParameterization foreign -> CLfmHullWhiteParameterization nocode#}
{#pointer *QlGJRGARCHModel as GJRGARCHModel foreign -> CGJRGARCHModel' nocode#}
{#pointer *QlHestonModel as HestonModel foreign -> CHestonModel' nocode#}
{#pointer *QlLocalVolTermStructure as LocalVolTermStructure foreign -> CLocalVolTermStructure' nocode#}
{#pointer *QlBrownianGeneratorFactory as BrownianGeneratorFactory foreign -> CBrownianGeneratorFactory' nocode#}
{#pointer *QlHestonSLVMCModel as HestonSLVMCModel foreign -> CHestonSLVMCModel' nocode#}
{#pointer *QlHestonSLVFDMModel as HestonSLVFDMModel foreign -> CHestonSLVFDMModel' nocode#}
{#pointer *HestonSLVFDMLogEntries as HestonSLVFDMLogEntries foreign -> CHestonSLVFDMLogEntries nocode#}
{#pointer *FdmSchemeDesc as QlFdmSchemeDesc foreign -> CFdmSchemeDesc nocode#}
{#pointer *QlBatesModel as BatesModel foreign -> CBatesModel' nocode#}
{#pointer *QlPiecewiseTimeDependentHestonModel as PiecewiseTimeDependentHestonModel foreign -> CPiecewiseTimeDependentHestonModel' nocode#}
{#pointer *QlShortRateModel as ShortRateModel foreign -> CShortRateModel' nocode#}
{#pointer *QlOneFactorAffineModel as OneFactorAffineModel foreign -> COneFactorAffineModel' nocode#}
{#pointer *QlLiborForwardModel as LiborForwardModel foreign -> CLiborForwardModel' nocode#}
{#pointer *QlHullWhite as HullWhite foreign -> CHullWhite' nocode#}
{#pointer *QlCalibratedModel as CalibratedModel foreign -> CCalibratedModel' nocode#}
{#pointer *QlG2 as G2 foreign -> CG2' nocode#}
{#pointer *QlShortRateDynamics as ShortRateDynamics foreign -> CShortRateDynamics' nocode#}
{#pointer *QlBatesDetJumpModel as BatesDetJumpModel foreign -> CBatesDetJumpModel' nocode#}
{#pointer *QlBatesDoubleExpDetJumpModel as BatesDoubleExpDetJumpModel foreign -> CBatesDoubleExpDetJumpModel' nocode#}
{#pointer *QlBatesDoubleExpModel as BatesDoubleExpModel foreign -> CBatesDoubleExpModel' nocode#}
{#pointer *QlGsr as Gsr foreign -> CGsr' nocode#}
{#pointer *QlMarkovFunctional as MarkovFunctional foreign -> CMarkovFunctional' nocode#}
{#pointer *QlAffineModel foreign -> CAffineModel' nocode#}
{#pointer *QlGaussian1dModel foreign -> CGaussian1dModel' nocode#}
{#pointer *QlSwapIndex as SwapIndex foreign -> CSwapIndex' nocode#}
{#pointer *QlSwaptionVolatilityStructure as SwaptionVolatilityStructure foreign -> CSwaptionVolatilityStructure' nocode#}
{#pointer *QlCalibrationHelper as CalibrationHelper foreign -> CCalibrationHelper' nocode#}
{#pointer *QlBlackCalibrationHelper as BlackCalibrationHelper foreign -> CBlackCalibrationHelper' nocode#}
{#pointer *QlSwaptionHelper as SwaptionHelper foreign -> CSwaptionHelper' nocode#}
{#pointer *QlFixedVsFloatingSwap as FixedVsFloatingSwap foreign -> CFixedVsFloatingSwap' nocode#}
{#pointer *QlSwaption as Swaption foreign -> CSwaption' nocode#}
{#pointer *QlGeneralizedBlackScholesProcess as GeneralizedBlackScholesProcess foreign -> CGeneralizedBlackScholesProcess' nocode#}
{#pointer *QlStochasticProcess1D as StochasticProcess1D foreign -> CStochasticProcess1D' nocode#}
{#pointer *QlStochasticProcess as StochasticProcess foreign -> CStochasticProcess' nocode#}
{#pointer *QlBlackProcess as BlackProcess foreign -> CBlackProcess' nocode#}
{#pointer *QlExtOUWithJumpsProcess as ExtOUWithJumpsProcess foreign -> CExtOUWithJumpsProcess' nocode#}
{#pointer *QlExtendedOrnsteinUhlenbeckProcess as ExtendedOrnsteinUhlenbeckProcess foreign -> CExtendedOrnsteinUhlenbeckProcess' nocode#}
{#pointer *QlGJRGARCHProcess as GJRGARCHProcess foreign -> CGJRGARCHProcess' nocode#}
{#pointer *QlHestonProcess as HestonProcess foreign -> CHestonProcess' nocode#}
{#pointer *QlBatesProcess as BatesProcess foreign -> CBatesProcess' nocode#}
{#pointer *QlHybridHestonHullWhiteProcess as HybridHestonHullWhiteProcess foreign -> CHybridHestonHullWhiteProcess' nocode#}
{#pointer *QlKlugeExtOUProcess as KlugeExtOUProcess foreign -> CKlugeExtOUProcess' nocode#}
{#pointer *QlLiborForwardModelProcess as LiborForwardModelProcess foreign -> CLiborForwardModelProcess' nocode#}
{#pointer *QlStochasticProcessArray as StochasticProcessArray foreign -> CStochasticProcessArray' nocode#}
{#pointer *QlVarianceGammaProcess as VarianceGammaProcess foreign -> CVarianceGammaProcess' nocode#}
{#pointer *QlMerton76Process as Merton76Process foreign -> CMerton76Process' nocode#}
{#pointer *QlHullWhiteProcess as HullWhiteProcess foreign -> CHullWhiteProcess' nocode#}
{#pointer *QlHullWhiteForwardProcess as HullWhiteForwardProcess foreign -> CHullWhiteForwardProcess' nocode#}
-- |Bates stochastic-volatility model: extends Heston with jumps in the underlying's return process.
{#fun qlBatesModel as batesModel{withBatesProcess*`BatesProcess',preErrorCheck-`String'errorCheck*-}->`BatesModel'peekBatesModel*#}
-- |Black-Karasinski short-rate model: d(ln r) = (theta(t) - a ln r) dt + sigma dW, with constant reversion @a@ and volatility @sigma@.
{#fun qlBlackKarasinski as blackKarasinski{withYieldTermStructure*`GenYieldTermStructure y',`Double' -- ^y
,`Double' -- ^sigma
,preErrorCheck-`String'errorCheck*-}->`ShortRateModel'peekShortRateModel*#}
-- |Cox-Ingersoll-Ross short-rate model: dr = k(theta - r) dt + sigma sqrt(r) dW.
{#fun qlCoxIngersollRoss as coxIngersollRoss{`Double' -- ^r0
,`Double' -- ^theta
,`Double' -- ^k
,`Double' -- ^sigma
,`Bool' -- ^withFellerConstraint
,preErrorCheck-`String'errorCheck*-}->`OneFactorAffineModel'peekOneFactorAffineModel*#}
-- |Extended CIR model: adds a deterministic term-structure-fitting shift to a standard Cox-Ingersoll-Ross process.
{#fun qlExtendedCoxIngersollRoss as extendedCoxIngersollRoss{withYieldTermStructure*`GenYieldTermStructure y',`Double' -- ^theta
,`Double' -- ^k
,`Double' -- ^sigma
,`Double' -- ^x0
,`Bool' -- ^withFellerConstraint
,preErrorCheck-`String'errorCheck*-}->`OneFactorAffineModel'peekOneFactorAffineModel*#}
-- |Two-additive-factor Gaussian (G2) short-rate model: the sum of two correlated Ornstein-Uhlenbeck factors.
{#fun qlG2 as g2{withYieldTermStructure*`GenYieldTermStructure y',`Double' -- ^y
,`Double' -- ^sigma
,`Double' -- ^b
,`Double' -- ^eta
,`Double' -- ^rho
,preErrorCheck-`String'errorCheck*-}->`G2'peekG2*#}
-- |The two-factor short-rate dynamics underlying a 'G2' model (@TwoFactorModel::dynamics()@).
{#fun qlG2Dynamics as g2Dynamics{withG2*`G2',preErrorCheck-`String'errorCheck*-}->`ShortRateDynamics'peekStandalone*#}
-- |Short rate implied by a 'ShortRateDynamics''s two state variables x, y at time t: @fitting_(t) + x + y@. At @x = y = 0@ this collapses to the model's fitting parameter @phi(t)@.
{#fun qlShortRateDynamicsShortRate as shortRate{withStandalone*`ShortRateDynamics'
,`Double' -- ^t
,`Double' -- ^x
,`Double' -- ^y
,preErrorCheck-`String'errorCheck*-}->`Double'#}
-- |Generalized Hull-White model: like 'hullWhite', but reversion and volatility are piecewise-linear functions of time given at @speedstructure@/@volstructure@ dates.
generalizedHullWhite :: GenYieldTermStructure y -> NonEmpty (Day, Double) -- ^speedstructure
-> NonEmpty (Day, Double) -- ^volstructure
-> IO ShortRateModel
generalizedHullWhite ts s v = qlGeneralizedHullWhite ts sd vd sq vq where {(sd, sq) = unzip (toList s); (vd, vq) = unzip (toList v)}
{#fun qlGeneralizedHullWhite{withYieldTermStructure*`GenYieldTermStructure y',withDayArray*`[Day]'&,withDayArray*`[Day]'&,withDoubleArray*`[Double]'&,withDoubleArray*`[Double]'&,preErrorCheck-`String'errorCheck*-}->`ShortRateModel'peekShortRateModel*#}
-- |GJR-GARCH stochastic-volatility model, extending GARCH(1,1) with an asymmetric response to negative return shocks.
{#fun qlGJRGARCHModel as gjrGarchModel{withGenStochasticProcess*`GJRGARCHProcess',preErrorCheck-`String'errorCheck*-}->`GJRGARCHModel'peekGJRGARCHModel*#}
-- |Heston stochastic-volatility model, calibrated from a 'HestonProcess'.
{#fun qlHestonModel as hestonModel{withHestonProcess*`GenHestonProcess hp',preErrorCheck-`String'errorCheck*-}->`HestonModel'peekHestonModel*#}
-- |Pseudo-random Mersenne-Twister Brownian increments for SLV Monte-Carlo calibration. Use a
-- fixed nonzero @seed@ for reproducible calibration output; QuantLib treats zero as entropy.
{#fun qlMTBrownianGeneratorFactory as mtBrownianGeneratorFactory{fromIntegral`Word' -- ^seed
,preErrorCheck-`String'errorCheck*-}->`BrownianGeneratorFactory'peekBrownianGeneratorFactory*#}
-- |Low-discrepancy Sobol Brownian increments for SLV Monte-Carlo calibration. Supply a fixed
-- nonzero @seed@ to make the resulting leverage function reproducible.
{#fun qlSobolBrownianGeneratorFactory as sobolBrownianGeneratorFactory{fromEnumC`SobolBrownianOrdering' -- ^ordering
,fromIntegral`Word' -- ^seed
,fromEnumC`SobolDirectionIntegers' -- ^directionIntegers
,preErrorCheck-`String'errorCheck*-}->`BrownianGeneratorFactory'peekBrownianGeneratorFactory*#}
-- |Monte-Carlo calibration of a Heston stochastic-local-volatility leverage function. The
-- trailing arguments mirror QuantLib's defaults explicitly; @mandatoryDates@ are inserted into
-- the calibration time grid.
{#fun qlHestonSLVMCModel as hestonSlvMcModel{withGenLocalVolTermStructure*`GenLocalVolTermStructure lv' -- ^localVol
,withHestonModel*`GenHestonModel hm' -- ^hestonModel
,withBrownianGeneratorFactory*`BrownianGeneratorFactory' -- ^brownianGeneratorFactory
,withDay*`Day' -- ^endDate
,fromIntegral`Word' -- ^timeStepsPerYear
,fromIntegral`Word' -- ^nBins
,fromIntegral`Word' -- ^calibrationPaths
,withDayArray*`[Day]'& -- ^mandatoryDates
,`Double' -- ^mixingFactor
,preErrorCheck-`String'errorCheck*-}->`HestonSLVMCModel'peekHestonSLVMCModel*#}
-- |Forces MC calibration if necessary and returns its local-volatility leverage function.
{#fun qlHestonSLVMCModelLeverageFunction{withHestonSLVMCModel*`HestonSLVMCModel' -- ^model
,preErrorCheck-`String'errorCheck*-}->`LocalVolTermStructure'peekLocalVolTermStructure*#}
-- |Fokker--Planck finite-difference calibration of a Heston stochastic-local-volatility
-- leverage function. @logging@ retains diagnostic density snapshots for
-- 'hestonSlvFdmLogEntries'; @mandatoryDates@ are added to its adaptive time grid.
hestonSlvFdmModel :: GenLocalVolTermStructure lv -> GenHestonModel hm -> Day
-> HestonSLVFokkerPlanckFdmParams -> Bool -> [Day] -> Double -> IO HestonSLVFDMModel
hestonSlvFdmModel localVol model endDate p logging mandatoryDates mixingFactor =
hestonSlvFdmModelRaw localVol model endDate
(hestonSlvXGrid p) (hestonSlvVGrid p) (hestonSlvTMaxStepsPerYear p) (hestonSlvTMinStepsPerYear p)
(hestonSlvTStepNumberDecay p) (hestonSlvNRannacherTimeSteps p) (hestonSlvPredictionCorrectionSteps p)
(hestonSlvX0Density p) (hestonSlvLocalVolEpsProb p) (hestonSlvMaxIntegrationIterations p)
(hestonSlvVLowerEps p) (hestonSlvVUpperEps p) (hestonSlvVMin p) (hestonSlvV0Density p)
(hestonSlvVLowerBoundDensity p) (hestonSlvVUpperBoundDensity p) (hestonSlvLeverageFctPropEps p)
(hestonSlvGreensAlgorithm p) (hestonSlvVarianceTransformation p) (hestonSlvSchemeDesc p)
logging mandatoryDates mixingFactor
{#fun qlHestonSLVFDMModel as hestonSlvFdmModelRaw{withGenLocalVolTermStructure*`GenLocalVolTermStructure lv' -- ^localVol
,withHestonModel*`GenHestonModel hm' -- ^hestonModel
,withDay*`Day' -- ^endDate
,fromIntegral`Word' -- ^xGrid
,fromIntegral`Word' -- ^vGrid
,fromIntegral`Word' -- ^tMaxStepsPerYear
,fromIntegral`Word' -- ^tMinStepsPerYear
,`Double' -- ^tStepNumberDecay
,fromIntegral`Word' -- ^nRannacherTimeSteps
,fromIntegral`Word' -- ^predictionCorrectionSteps
,`Double' -- ^x0Density
,`Double' -- ^localVolEpsProb
,fromIntegral`Word' -- ^maxIntegrationIterations
,`Double' -- ^vLowerEps
,`Double' -- ^vUpperEps
,`Double' -- ^vMin
,`Double' -- ^v0Density
,`Double' -- ^vLowerBoundDensity
,`Double' -- ^vUpperBoundDensity
,`Double' -- ^leverageFctPropEps
,fromEnumC`HestonSLVGreensAlgorithm' -- ^greensAlgorithm
,fromEnumC`HestonSLVVarianceTransformation' -- ^trafoType
,withFdmSchemeDesc*`FdmScheme' -- ^schemeDesc
,`Bool' -- ^logging
,withDayArray*`[Day]'& -- ^mandatoryDates
,`Double' -- ^mixingFactor
,preErrorCheck-`String'errorCheck*-}->`HestonSLVFDMModel'peekHestonSLVFDMModel*#}
-- |Forces FDM calibration if necessary and returns its local-volatility leverage function.
{#fun qlHestonSLVFDMModelLeverageFunction{withHestonSLVFDMModel*`HestonSLVFDMModel' -- ^model
,preErrorCheck-`String'errorCheck*-}->`LocalVolTermStructure'peekLocalVolTermStructure*#}
-- |Copies retained FDM density diagnostics. Returns @[]@ when the model was built with
-- @logging = False@. Calling this makes one fresh QuantLib diagnostic calculation, then decodes
-- its owned snapshot without retaining the model's mesh objects.
hestonSlvFdmLogEntries :: HestonSLVFDMModel -> IO [HestonSLVFDMLogEntry]
hestonSlvFdmLogEntries model = do
snapshot <- hestonSlvFdmLogEntriesSnapshot model
let n = hestonSlvFdmLogEntriesSize snapshot
mapM (hestonSlvFdmLogEntry snapshot) (genericTake n [0 ..])
hestonSlvFdmLogEntry :: HestonSLVFDMLogEntries -> Word -> IO HestonSLVFDMLogEntry
hestonSlvFdmLogEntry snapshot i = do
t <- hestonSlvFdmLogEntriesTime snapshot i
x <- hestonSlvFdmLogEntriesSpotGrid snapshot i
v <- hestonSlvFdmLogEntriesVarianceGrid snapshot i
(r, c, d) <- hestonSlvFdmLogEntriesDensity snapshot i
pure $ HestonSLVFDMLogEntry t x v (RealMatrix r c d)
{#fun qlHestonSLVFDMModelLogEntries as hestonSlvFdmLogEntriesSnapshot{withHestonSLVFDMModel*`HestonSLVFDMModel',preErrorCheck-`String'errorCheck*-}->`HestonSLVFDMLogEntries'peekHestonSLVFDMLogEntries*#}
{#fun pure qlHestonSLVFDMLogEntriesSize as hestonSlvFdmLogEntriesSize{withHestonSLVFDMLogEntries*`HestonSLVFDMLogEntries'}->`Word'fromIntegral#}
{#fun qlHestonSLVFDMLogEntriesTime as hestonSlvFdmLogEntriesTime{withHestonSLVFDMLogEntries*`HestonSLVFDMLogEntries',fromIntegral`Word',preErrorCheck-`String'errorCheck*-}->`Double'#}
{#fun qlHestonSLVFDMLogEntriesSpotGrid as hestonSlvFdmLogEntriesSpotGrid{withHestonSLVFDMLogEntries*`HestonSLVFDMLogEntries',fromIntegral`Word',preArray-`RealVector'&peekRealVector*,preErrorCheck-`String'errorCheck*-}->`()'#}
{#fun qlHestonSLVFDMLogEntriesVarianceGrid as hestonSlvFdmLogEntriesVarianceGrid{withHestonSLVFDMLogEntries*`HestonSLVFDMLogEntries',fromIntegral`Word',preArray-`RealVector'&peekRealVector*,preErrorCheck-`String'errorCheck*-}->`()'#}
{#fun qlHestonSLVFDMLogEntriesDensity as hestonSlvFdmLogEntriesDensity{withHestonSLVFDMLogEntries*`HestonSLVFDMLogEntries',fromIntegral`Word',prePtr-`Word'peekWord*,prePtr-`Word'peekWord*,preArray-`RealVector'&peekRealVector*,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |Single-factor Hull-White (extended Vasicek) short-rate model: dr = (theta(t) - a r) dt + sigma dW, fitted to the given term structure.
{#fun qlHullWhite as hullWhite{withYieldTermStructure*`GenYieldTermStructure y',`Double' -- ^y
,`Double' -- ^sigma
,preErrorCheck-`String'errorCheck*-}->`HullWhite'peekHullWhite*#}
-- |Futures convexity bias (difference between futures implied rate and forward rate), per G. Kirikos, D. Novak, \"Convexity Conundrums\", Risk Magazine, March 1997. @t@/@T@ are in yearfraction using the deposit day counter, @futurePrice@ is the futures' market price.
-- Not 'pure': 'HullWhite.convexityBias' can throw ('QL_REQUIRE' on its inputs), and letting a C++
-- exception unwind across the FFI boundary from an 'unsafePerformIO'-backed pure binding is undefined
-- behavior, so this needs the same 'char **e'/'preErrorCheck' error channel as any other throwing call.
{#fun qlHullWhiteConvexityBias as convexityBias{`Double' -- ^futurePrice
,`Double' -- ^t
,`Double' -- ^T
,`Double' -- ^sigma
,`Double' -- ^a
,preErrorCheck-`String'errorCheck*-}->`Double'#}
-- |Marks the reversion (@a@) fixed and volatility (@sigma@) free for 'calibrate''s @fixParameters@ argument. Mirrors @HullWhite::FixedReversion()@.
fixedReversion :: [Bool]
fixedReversion = [True, False]
-- |One-factor GSR model (formulated in the forward measure), with an initial volatility and
-- piecewise-constant changes at the given dates, plus a single constant reversion.
gsr :: GenYieldTermStructure y -> GenQuote q1 -> [(Day, GenQuote q1)] -> GenQuote q2 -> Double -> IO Gsr
gsr ts initialVol subsequentVols reversion horizon =
qlGsr ts dates (initialVol : vols) reversion horizon
where (dates, vols) = unzip subsequentVols
{#fun qlGsr{withYieldTermStructure*`GenYieldTermStructure y',withDayArray*`[Day]'& -- ^volstepdates
,withQuoteArray*`[GenQuote q1]'& -- ^volatilities
,withQuote*`GenQuote q2' -- ^reversion
,`Double' -- ^T
,preErrorCheck-`String'errorCheck*-}->`Gsr'peekGsr*#}
-- |Volatility step values, as calibrated so far.
{#fun qlGsrVolatility{withGenCalibratedModel*`Gsr',preArray-`[Double]'&peekDoubleArray*,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |The calibration mask ('calibrate''s @fixParameters@) that fixes every model parameter except
-- the volatility at step index @i@ (0-based) -- a ready-made @fixParameters@ argument for
-- calibrating that one volatility in isolation.
{#fun qlGsrMoveVolatility as moveVolatility{withGenCalibratedModel*`Gsr'
,fromIntegral`Word' -- ^i
,preArray-`[Bool]'&peekBoolArray*,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |The calibration mask that fixes every model parameter except the reversion at index @i@
-- (0-based) -- the reversion counterpart of 'moveVolatility'.
{#fun qlGsrMoveReversion as moveReversion{withGenCalibratedModel*`Gsr'
,fromIntegral`Word' -- ^i
,preArray-`[Bool]'&peekBoolArray*,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |Iteratively calibrates the volatility step values, one at a time, to the given helpers (assumed to have step dates matching the model's volatility step dates).
{#fun qlGsrCalibrateVolatilitiesIterative as calibrateVolatilitiesIterative{withGenCalibratedModel*`Gsr',withBlackCalibrationHelperArray*`[GenBlackCalibrationHelper bch]'&,withOptimizationMethod*`OptimizationMethod',withEndCriteria*`EndCriteria'
,withMaybeConstraint*`Maybe Constraint'
,withDoubleArray*`[Double]'&
,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |Markov-functional interest-rate model, calibrated to a swaption volatility cube against @swapIndexBase@.
markovFunctional :: GenYieldTermStructure y -> Double -- ^reversion
-> Double -- ^initial volatility
-> [(Day, Double)] -- ^subsequent volatility steps
-> SwaptionVolatilityStructure
-> NonEmpty (Day, (Word, TimeUnit)) -- ^swaption expiry/tenor calibration points
-> GenSwapIndex sidx -- ^swapIndexBase
-> Word -- ^yGridPoints
-> IO MarkovFunctional
markovFunctional ts reversion initialVol steps svol points = qlMarkovFunctional ts reversion dates (initialVol : vols) svol expiries tq tu
where (dates, vols) = unzip steps
(expiries, tenors) = unzip (toList points)
(tq, tu) = unzip tenors
{#fun qlMarkovFunctional{withYieldTermStructure*`GenYieldTermStructure y',`Double'
,withDayArray*`[Day]'&,withDoubleArray*`[Double]'&
,withSwaptionVolatilityStructure*`GenSwaptionVolatilityStructure sv'
,withDayArray*`[Day]'&
,withIntArray*`[Word]'&,withEnumArray*`[TimeUnit]'&
,withSwapIndex*`GenSwapIndex sidx'
,fromIntegral`Word'
,preErrorCheck-`String'errorCheck*-}->`MarkovFunctional'peekMarkovFunctional*#}
-- |Markov-functional interest-rate model, calibrated to a caplet volatility structure against @iborIndex@.
markovFunctionalCaplet :: GenYieldTermStructure y -> Double -> Double -> [(Day, Double)]
-> OptionletVolatilityStructure -> NonEmpty Day -> GenIborIndex ibor -> Word -> IO MarkovFunctional
markovFunctionalCaplet ts reversion initialVol steps capletVol expiries ibor gridPoints =
qlMarkovFunctionalCaplet ts reversion dates (initialVol : vols) capletVol (toList expiries) ibor gridPoints
where (dates, vols) = unzip steps
{#fun qlMarkovFunctionalCaplet{withYieldTermStructure*`GenYieldTermStructure y',`Double' -- ^reversion
,withDayArray*`[Day]'& -- ^volstepdates
,withDoubleArray*`[Double]'& -- ^volatilities
,withOptionletVolatilityStructure*`OptionletVolatilityStructure' -- ^capletVol
,withDayArray*`[Day]'& -- ^capletExpiries
,withIborIndex*`GenIborIndex ibor' -- ^iborIndex
,fromIntegral`Word' -- ^yGridPoints
,preErrorCheck-`String'errorCheck*-}->`MarkovFunctional'peekMarkovFunctional*#}
-- |Volatility step values, as calibrated so far.
{#fun qlMarkovFunctionalVolatility{withGenCalibratedModel*`MarkovFunctional',preArray-`[Double]'&peekDoubleArray*,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |Discount factor at time @t@ under the model's own fitted curve -- @AffineModel::discount@.
-- Not 'pure': depends on the model's 'YieldTermStructure' handle, which can be relinked after
-- construction (see 'discountBond' below for the same caveat).
{#fun qlAffineModelDiscount as discount{withStandalone*`AffineModel'
,`Double' -- ^t
,preErrorCheck-`String'errorCheck*-}->`Double'#}
-- |Price of a discount bond paying 1 at @maturity@, given the model's state @factors@ at time
-- @now@ -- @AffineModel::discountBond(Time,Time,Array)@. @factors@ is model-specific: a
-- one-element list of the short rate for 'OneFactorAffineModel'\/'HullWhite', a two-element list
-- for G2, or ignored by 'LiborForwardModel'. Not 'pure' for the same relinkable-curve reason as
-- 'discount'.
{#fun qlAffineModelDiscountBond as discountBond{withStandalone*`AffineModel'
,`Double' -- ^now
,`Double' -- ^maturity
,withDoubleArray*`[Double]'& -- ^factors
,preErrorCheck-`String'errorCheck*-}->`Double'#}
{#fun qlAffineModelDiscountBondOption as discountBondOption_{withStandalone*`AffineModel'
,fromEnumC`OptionType' -- ^type
,`Double' -- ^strike
,`Double' -- ^maturity
,`Bool' -- ^haveBondStart
,`Double' -- ^bondStart
,`Double' -- ^bondMaturity
,preErrorCheck-`String'errorCheck*-}->`Double'#}
-- |Analytic price of a European option on a discount bond paying 1 at @bondMaturity@. 'Nothing'
-- starts the bond at option expiry; 'Just' supplies a distinct bond start. Only 'HullWhite'
-- distinguishes the two upstream overloads.
discountBondOption :: AffineModel -> OptionType -> Double -> Double -> Maybe Double -> Double -> IO Double
discountBondOption model typ strike maturity bondStart bondMaturity =
discountBondOption_ model typ strike maturity (maybe False (const True) bondStart) (fromMaybe 0 bondStart) bondMaturity
-- |Numeraire value at @referenceDate@, conditional on the standardized state variable @y@
-- (0 = the model's expected path). @yts@ overrides the model's own term structure for
-- discounting when given, otherwise the model's own curve is used.
{#fun qlGaussian1dModelNumeraire as numeraire{withStandalone*`Gaussian1dModel'
,withDay*`Day' -- ^referenceDate
,`Double' -- ^y
,withMaybeYieldTermStructure*`Maybe (GenYieldTermStructure y)' -- ^yts
,preErrorCheck-`String'errorCheck*-}->`Double'#}
-- |Price at @referenceDate@ (default: the evaluation date) of a zero-coupon bond paying 1 at
-- @maturity@, conditional on the standardized state variable @y@ (0 = the model's expected
-- path). @yts@ overrides the model's own term structure for discounting when given.
{#fun qlGaussian1dModelZerobond as gaussian1dZerobond{withStandalone*`Gaussian1dModel'
,withDay*`Day' -- ^maturity
,withMaybeDay*`Maybe Day' -- ^referenceDate
,`Double' -- ^y
,withMaybeYieldTermStructure*`Maybe (GenYieldTermStructure y)' -- ^yts
,preErrorCheck-`String'errorCheck*-}->`Double'#}
-- |Price of a European option of @type@, expiring at @expiry@ with @strike@, on a zero-coupon
-- bond that itself pays 1 at @maturity@ and settles at @valueDate@, conditional on the
-- standardized state variable @y@ at @referenceDate@ (default: the evaluation date). @yts@
-- overrides the model's own discounting curve when given. The option payoff is evaluated on a
-- grid of @yGridPoints@ points spanning @yStdDevs@ standard deviations of @y@;
-- @extrapolatePayoff@\/@flatPayoffExtrapolation@ control payoff extrapolation beyond the grid.
{#fun qlGaussian1dModelZerobondOption as gaussian1dZerobondOption{withStandalone*`Gaussian1dModel'
,fromEnumC`OptionType' -- ^type
,withDay*`Day' -- ^expiry
,withDay*`Day' -- ^valueDate
,withDay*`Day' -- ^maturity
,`Double' -- ^strike
,withMaybeDay*`Maybe Day' -- ^referenceDate
,`Double' -- ^y
,withMaybeYieldTermStructure*`Maybe (GenYieldTermStructure y)' -- ^yts
,`Double' -- ^yStdDevs
,fromIntegral`Word' -- ^yGridPoints
,`Bool' -- ^extrapolatePayoff
,`Bool' -- ^flatPayoffExtrapolation
,preErrorCheck-`String'errorCheck*-}->`Double'#}
-- |Forward rate for @iborIdx@ fixing on @fixing@, conditional on the standardized state
-- variable @y@ at @referenceDate@ (default: the evaluation date). @iborIdx@ defaults to the
-- model's own term structure's natural index when omitted.
{#fun qlGaussian1dModelForwardRate as gaussian1dForwardRate{withStandalone*`Gaussian1dModel'
,withDay*`Day' -- ^fixing
,withMaybeDay*`Maybe Day' -- ^referenceDate
,`Double' -- ^y
,withMaybeIborIndex*`Maybe (GenIborIndex ibor)' -- ^iborIdx
,preErrorCheck-`String'errorCheck*-}->`Double'#}
-- |Fair swap rate for a swap on @swapIdx@ fixing on @fixing@ with tenor @tenor@, conditional on
-- the standardized state variable @y@ at @referenceDate@ (default: the evaluation date).
{#fun qlGaussian1dModelSwapRate as gaussian1dSwapRate{withStandalone*`Gaussian1dModel'
,withDay*`Day' -- ^fixing
,fromEnumQuantity`(Int,TimeUnit)'& -- ^tenor
,withMaybeDay*`Maybe Day' -- ^referenceDate
,`Double' -- ^y
,withMaybeSwapIndex*`Maybe (GenSwapIndex sidx)' -- ^swapIdx
,preErrorCheck-`String'errorCheck*-}->`Double'#}
-- |Annuity (present value of a 1bp fixed leg) of a swap on @swapIdx@ fixing on @fixing@ with
-- tenor @tenor@, conditional on the standardized state variable @y@ at @referenceDate@
-- (default: the evaluation date).
{#fun qlGaussian1dModelSwapAnnuity as gaussian1dSwapAnnuity{withStandalone*`Gaussian1dModel'
,withDay*`Day' -- ^fixing
,fromEnumQuantity`(Int,TimeUnit)'& -- ^tenor
,withMaybeDay*`Maybe Day' -- ^referenceDate
,`Double' -- ^y
,withMaybeSwapIndex*`Maybe (GenSwapIndex sidx)' -- ^swapIdx
,preErrorCheck-`String'errorCheck*-}->`Double'#}
-- |Grid of values for the standardized state variable at time @T@, conditional on the
-- variable being @y@ at time @t@, spanning @yStdDevs@ standard deviations with
-- @2*gridPoints+1@ points.
{#fun qlGaussian1dModelYGrid as gaussian1dYGrid{withStandalone*`Gaussian1dModel'
,`Double' -- ^yStdDevs
,fromIntegral`Int' -- ^gridPoints
,`Double' -- ^bigT
,`Double' -- ^t
,`Double' -- ^y
,preArray-`RealVector'&peekRealVector*
,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |The model's own state process. Throws if the model was constructed without one set.
{#fun qlGaussian1dModelStateProcess as stateProcess{withStandalone*`Gaussian1dModel',preErrorCheck-`String'errorCheck*-}->`StochasticProcess1D'peekStochasticProcess1D*#}
-- |Models that expose a calibrated local-volatility leverage function.
class HasLeverageFunction a where
leverageFunction :: a -> IO LocalVolTermStructure
instance HasLeverageFunction HestonSLVMCModel where
leverageFunction = qlHestonSLVMCModelLeverageFunction
instance HasLeverageFunction HestonSLVFDMModel where
leverageFunction = qlHestonSLVFDMModelLeverageFunction
-- |Models that expose their calibrated volatility step values.
class HasVolatilities a where
volatilities :: a -> IO [Double]
instance HasVolatilities Gsr where
volatilities = qlGsrVolatility
instance HasVolatilities MarkovFunctional where
volatilities = qlMarkovFunctionalVolatility
-- |Variance Gamma model for the underlying's log-return process (Madan-Carr-Chang).
{#fun qlVarianceGammaModel as varianceGammaModel{withGenStochasticProcess1D*`VarianceGammaProcess',preErrorCheck-`String'errorCheck*-}->`CalibratedModel'peekCalibratedModel*#}
-- |Vasicek short-rate model: dr = a(b - r) dt + sigma dW, with an optional risk premium @lambda@.
{#fun qlVasicek as vasicek{`Double' -- ^r0
,`Double' -- ^a
,`Double' -- ^b
,`Double' -- ^sigma
,`Double' -- ^lambda
,preErrorCheck-`String'errorCheck*-}->`OneFactorAffineModel'peekOneFactorAffineModel*#}
-- |Libor market (BGM) forward-rate model, built from a 'LiborForwardModelProcess' plus volatility and correlation models.
{#fun qlLiborForwardModel as liborForwardModel{withGenStochasticProcess*`LiborForwardModelProcess',withLmVolatilityModel*`LmVolatilityModel',withLmCorrelationModel*`LmCorrelationModel',preErrorCheck-`String'errorCheck*-}->`LiborForwardModel'peekLiborForwardModel*#}
-- |Initial value of the discrete forward rate indexed by /alpha/, as seen from time index /beta/
-- (both indices into the process's tenor structure): the model's calibration input @S_0(alpha,beta)@.
{#fun qlLiborForwardModelS0 as liborForwardModelS0{withGenCalibratedModel*`LiborForwardModel'
,fromIntegral`Word' -- ^alpha
,fromIntegral`Word' -- ^beta
,preErrorCheck-`String'errorCheck*-}->`Double'#}
-- |Hull-White caplet-volatility parameterization for a Libor forward model. The correlation
-- matrix and factor count are explicit, mirroring QuantLib's defaulted constructor arguments;
-- pass an empty matrix and @1@ for its standard one-factor default.
lfmHullWhiteParameterization :: LiborForwardModelProcess -> GenOptionletVolatilityStructure ov
-> Matrix Double -- ^correlation
-> Word -- ^factors
-> IO LfmHullWhiteParameterization
lfmHullWhiteParameterization process capletVol (Matrix rows cols values) factors = qlLfmHullWhiteParameterization process capletVol rows cols values factors
{#fun qlLfmHullWhiteParameterization{withGenStochasticProcess*`LiborForwardModelProcess' -- ^process
,withOptionletVolatilityStructure*`GenOptionletVolatilityStructure ov' -- ^capletVol
,fromIntegral`Word' -- ^correlationRows
,fromIntegral`Word' -- ^correlationColumns
,withDoubleArrayRaw*`[Double]' -- ^correlation
,fromIntegral`Word' -- ^factors
,preErrorCheck-`String'errorCheck*-}->`LfmHullWhiteParameterization'peekLfmHullWhiteParameterization*#}
-- |installs the covariance parameterization a 'LiborForwardModelProcess' evolves under.
--
-- Mandatory before the process is used for anything dynamic: a freshly constructed
-- 'QuantLib.Process.liborForwardModelProcess' holds no parameterization, and
-- 'QuantLib.Process.drift', 'QuantLib.Process.diffusion', 'QuantLib.Process.evolve',
-- 'QuantLib.Process.factors' and 'QuantLib.Method.pathGenerator' all dereference it.
-- 'QuantLib.Process.discountBond', the fixing/accrual times and
-- 'QuantLib.Process.cashFlows' do not, and work without it.
{#fun qlLiborForwardModelProcessSetCovarParam as setCovarParam{withGenStochasticProcess*`LiborForwardModelProcess' -- ^process
,withStandalone*`LfmHullWhiteParameterization' -- ^param
,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |Instantaneous covariance matrix at @t@ for the supplied forward-rate state @x@.
lfmHullWhiteCovariance :: LfmHullWhiteParameterization -> Double -> [Double] -> IO (Matrix Double)
lfmHullWhiteCovariance p t x = toMatrixDouble <$> qlLfmHullWhiteCovariance p t x
where toMatrixDouble (r, c, d) = Matrix r c d
{#fun qlLfmHullWhiteCovariance{withStandalone*`LfmHullWhiteParameterization' -- ^parameterization
,`Double' -- ^t
,withDoubleArray*`[Double]'& -- ^x
,prePtr-`Word'peekWord*,prePtr-`Word'peekWord*,preArray-`[Double]'&peekDoubleArray*
,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |Calibrate to a set of market instruments (caps/swaptions)
-- An additional constraint can be passed which must be satisfied in addition to the constraints of the model.
calibrate :: GenCalibratedModel m -> NonEmpty (GenCalibrationHelper ch, Double) -- ^(instrument, weight)
-> OptimizationMethod -> EndCriteria -> Maybe Constraint
-> [Bool] -- ^fixParameters, e.g. 'fixedReversion'; @[]@ leaves nothing fixed
-> IO ()
calibrate m h o e c fp = qlCalibratedModelCalibrate m hh hw o e c fp where (hh, hw) = unzip (toList h)
{#fun qlCalibratedModelCalibrate{withCalibratedModel*`GenCalibratedModel m',withCalibrationHelperArray*`[GenCalibrationHelper ch]'&,withDoubleArray*`[Double]'&
,withOptimizationMethod*`OptimizationMethod',withEndCriteria*`EndCriteria',withMaybeConstraint*`Maybe Constraint',withBoolArray*`[Bool]'&,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |Objective function value at @params@ for the given calibration instruments.
{#fun qlCalibratedModelValue as value{withCalibratedModel*`GenCalibratedModel m',withDoubleArray*`[Double]'&,withCalibrationHelperArray*`[GenCalibrationHelper ch]'&,preErrorCheck-`String'errorCheck*-}->`Double'#}
-- |Calibration helper for an at-the-money interest-rate cap.
{#fun qlCapHelper as capHelper{fromEnumQuantity`(Word,TimeUnit)'& -- ^length
,withQuote*`GenQuote q' -- ^volatility
,withIborIndex*`GenIborIndex ibor',`Frequency' -- ^fixedLegFrequency
,withDayCounter*`DayCounter',`Bool' -- ^includeFirstSwaplet
,withYieldTermStructure*`GenYieldTermStructure y',`CalibrationErrorType'
,`VolatilityType' -- ^type
,`Double' -- ^shift
,preErrorCheck-`String'errorCheck*-}->`BlackCalibrationHelper'peekBlackCalibrationHelper*#}
-- |Calibration helper for the Heston model, from a European option's market volatility.
{#fun qlHestonModelHelper as hestonModelHelper{fromEnumQuantity`(Word,TimeUnit)'& -- ^maturity
,withCalendar*`Calendar',withQuote*`GenQuote q1' -- ^s0
,`Double' -- ^strikePrice
,withQuote*`GenQuote q2' -- ^volatility
,withYieldTermStructure*`GenYieldTermStructure y1' -- ^riskFreeRate
,withYieldTermStructure*`GenYieldTermStructure y2' -- ^dividendYield
,`CalibrationErrorType',preErrorCheck-`String'errorCheck*-}->`BlackCalibrationHelper'peekBlackCalibrationHelper*#}
-- |A swaption helper's exercise and underlying-swap span.
data SwaptionSpan
= SpanTenors !(Word, TimeUnit) !(Word, TimeUnit) -- ^maturity, length
| SpanFromDate !Day !(Word, TimeUnit) -- ^exerciseDate, length
| SpanDates !Day !Day -- ^exerciseDate, endDate
deriving (Eq, Show)
-- |Calibration helper for a swaption, over the given exercise/underlying span.
swaptionHelper :: SwaptionSpan -> GenQuote q -> GenIborIndex ibor -> (Word, TimeUnit)
-> DayCounter -> DayCounter -> GenYieldTermStructure y -> CalibrationErrorType
-> Maybe Double -> Double -> VolatilityType -> Double -> Maybe Word -> RateAveragingType
-> IO SwaptionHelper
swaptionHelper span' = case span' of
SpanTenors m l -> swaptionHelperTenorsRaw m l
SpanFromDate d l -> swaptionHelperFromDateRaw d l
SpanDates d e -> swaptionHelperFromDatesRaw d e
{#fun qlSwaptionHelper as swaptionHelperTenorsRaw{fromEnumQuantity`(Word,TimeUnit)'& -- ^maturity
,fromEnumQuantity`(Word,TimeUnit)'& -- ^length
,withQuote*`GenQuote q' -- ^maturity
,withIborIndex*`GenIborIndex ibor',fromEnumQuantity`(Word,TimeUnit)'& -- ^fixedLegTenor
,withDayCounter*`DayCounter' -- ^fixedLegDayCounter
,withDayCounter*`DayCounter' -- ^floatingLegDayCounter
,withYieldTermStructure*`GenYieldTermStructure y',`CalibrationErrorType'
,fromMaybeDouble`Maybe Double' -- ^strike
,`Double' -- ^nominal
,`VolatilityType' -- ^type
,`Double' -- ^shift
,fromMaybeInt`Maybe Word' -- ^settlementDays
,`RateAveragingType' -- ^averagingMethod
,preErrorCheck-`String'errorCheck*-}->`SwaptionHelper'peekSwaptionHelper*#}
-- |Like 'swaptionHelper', but the option's exercise is given as an explicit date rather than a maturity 'Period'.
{#fun qlSwaptionHelperFromDate as swaptionHelperFromDateRaw{withDay*`Day' -- ^exerciseDate
,fromEnumQuantity`(Word,TimeUnit)'& -- ^length
,withQuote*`GenQuote q' -- ^maturity
,withIborIndex*`GenIborIndex ibor',fromEnumQuantity`(Word,TimeUnit)'& -- ^fixedLegTenor
,withDayCounter*`DayCounter' -- ^fixedLegDayCounter
,withDayCounter*`DayCounter' -- ^floatingLegDayCounter
,withYieldTermStructure*`GenYieldTermStructure y',`CalibrationErrorType'
,fromMaybeDouble`Maybe Double' -- ^strike
,`Double' -- ^nominal
,`VolatilityType' -- ^type
,`Double' -- ^shift
,fromMaybeInt`Maybe Word' -- ^settlementDays
,`RateAveragingType' -- ^averagingMethod
,preErrorCheck-`String'errorCheck*-}->`SwaptionHelper'peekSwaptionHelper*#}
-- |Like 'swaptionHelper', but both the option's exercise and the underlying swap's end are given as explicit dates.
{#fun qlSwaptionHelperFromDates as swaptionHelperFromDatesRaw{withDay*`Day' -- ^exerciseDate
,withDay*`Day' -- ^endDate
,withQuote*`GenQuote q' -- ^maturity
,withIborIndex*`GenIborIndex ibor',fromEnumQuantity`(Word,TimeUnit)'& -- ^fixedLegTenor
,withDayCounter*`DayCounter' -- ^fixedLegDayCounter
,withDayCounter*`DayCounter' -- ^floatingLegDayCounter
,withYieldTermStructure*`GenYieldTermStructure y',`CalibrationErrorType'
,fromMaybeDouble`Maybe Double' -- ^strike
,`Double' -- ^nominal
,`VolatilityType' -- ^type
,`Double' -- ^shift
,fromMaybeInt`Maybe Word' -- ^settlementDays
,`RateAveragingType' -- ^averagingMethod
,preErrorCheck-`String'errorCheck*-}->`SwaptionHelper'peekSwaptionHelper*#}
-- |The 'QuantLib.Instrument.Swap.Swaption' this helper prices internally to compute 'modelValue'.
{#fun qlSwaptionHelperSwaption as helperSwaption{withSwaptionHelper*`SwaptionHelper',preErrorCheck-`String'errorCheck*-}->`Swaption'peekSwaption*#}
-- |Times relevant to pricing this calibration helper's instrument, to be added to the model's evolution time grid.
{#fun qlBlackCalibrationHelperTimes as times{withBlackCalibrationHelper*`GenBlackCalibrationHelper bch',preArray-`[Double]'&peekDoubleArray*,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |Returns array of arguments on which calibration is done.
{#fun qlCalibratedModelParams as params{withCalibratedModel*`GenCalibratedModel m',preArray-`[Double]'&peekDoubleArray*,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |Black price given a volatility.
{#fun qlBlackCalibrationHelperBlackPrice as blackPrice{withBlackCalibrationHelper*`GenBlackCalibrationHelper bch',`Double' -- ^volatility
,preErrorCheck-`String'errorCheck*-}->`Double'#}
-- |returns the error resulting from the model valuation
{#fun qlBlackCalibrationHelperCalibrationError as calibrationError{withBlackCalibrationHelper*`GenBlackCalibrationHelper bch',preErrorCheck-`String'errorCheck*-}->`Double'#}
-- |Black volatility implied by the model.
{#fun qlBlackCalibrationHelperImpliedVolatility as impliedVolatility{withBlackCalibrationHelper*`GenBlackCalibrationHelper bch',`Double' -- ^targetValue
,`Double' -- ^accuracy
,fromIntegral`Word' -- ^maxEvaluations
,`Double' -- ^minVol
,`Double' -- ^maxVol
,preErrorCheck-`String'errorCheck*-}->`Double'#}
-- |returns the actual price of the instrument (from volatility)
{#fun qlBlackCalibrationHelperMarketValue as marketValue{withBlackCalibrationHelper*`GenBlackCalibrationHelper bch',preErrorCheck-`String'errorCheck*-}->`Double'#}
-- |returns the price of the instrument according to the model
{#fun qlBlackCalibrationHelperModelValue as modelValue{withBlackCalibrationHelper*`GenBlackCalibrationHelper bch',preErrorCheck-`String'errorCheck*-}->`Double'#}
-- |The quoted market volatility this helper was built with.
{#fun qlBlackCalibrationHelperVolatility as volatility{withBlackCalibrationHelper*`GenBlackCalibrationHelper bch',preErrorCheck-`String'errorCheck*-}->`Quote'peekQuote*#}
-- |Sets the pricing engine used to compute this calibration helper's model value.
{#fun qlBlackCalibrationHelperSetPricingEngine as setPricingEngine{withBlackCalibrationHelper*`GenBlackCalibrationHelper bch',withPricingEngine*`PricingEngine',preErrorCheck-`String'errorCheck*-}->`()'#}
{#enum Garch11Mode{} deriving(Show, Eq, Read)#}
{#pointer *Garch11 foreign -> CGarch11 nocode#}
-- |Direct-parameter GARCH(1,1) model: @vl@ is the long-term (unconditional) volatility: the
-- model's persistence @gamma = 1 - alpha - beta@ and @omega = vl * gamma@ are derived from it.
-- Does not calibrate; use 'garch11Calibrated' to fit alpha\/beta\/vl to an observed series.
{#fun qlGarch11 as garch11{`Double' -- ^alpha
,`Double' -- ^beta
,`Double' -- ^vl (long-term volatility)
,preErrorCheck-`String'errorCheck*-
}->`Garch11'peekGarch11*#}
-- |Calibrates a GARCH(1,1) model to an observed return series via maximum likelihood. All four
-- 'Garch11Mode' values converge to the same maximum-likelihood fit for a well-behaved series --
-- they differ only in the initial guess ('MomentMatchingGuess'\/'GammaGuess'), or run both and
-- keep the better ('BestOfTwo', the upstream default) or optimize from each in turn
-- ('DoubleOptimization'). There is no way from Haskell to supply a custom
-- 'OptimizationMethod'\/'EndCriteria' or to re-calibrate an existing model in place (upstream's
-- @calibrate@ overloads are mutators on an already-constructed object); construct a fresh model
-- if a different fit is needed.
garch11Calibrated :: NonEmpty (Day, Double) -- ^observed return series
-> Garch11Mode -> IO Garch11
garch11Calibrated series = qlGarch11Calibrated dates vals
where (dates, vals) = unzip (toList series)
{#fun qlGarch11Calibrated{withDayArray*`[Day]'&,withDoubleArray*`[Double]'&,`Garch11Mode'
,preErrorCheck-`String'errorCheck*-}->`Garch11'peekGarch11*#}
-- |the calibrated (or constructor-supplied) alpha coefficient
{#fun pure qlGarch11Alpha as alpha{withGarch11*`Garch11'}->`Double'#}
-- |the calibrated (or constructor-supplied) beta coefficient
{#fun pure qlGarch11Beta as beta{withGarch11*`Garch11'}->`Double'#}
-- |the calibrated (or derived) omega coefficient, @vl * (1 - alpha - beta)@
{#fun pure qlGarch11Omega as omega{withGarch11*`Garch11'}->`Double'#}
-- |the calibrated (or constructor-supplied) long-term volatility
{#fun pure qlGarch11LtVol as longTermVolatility{withGarch11*`Garch11'}->`Double'#}
-- |the log-likelihood of the calibrated fit; @0@ for a direct-parameter ('garch11') model
{#fun pure qlGarch11LogLikelihood as logLikelihood{withGarch11*`Garch11'}->`Double'#}
-- |one-step-ahead variance forecast: @gamma*vl + alpha*r^2 + beta*sigma2@, given the latest
-- return @r@ and the previous step's variance @sigma2@.
{#fun pure qlGarch11Forecast as forecast{withGarch11*`Garch11'
,`Double' -- ^r
,`Double' -- ^sigma2
}->`Double'#}
-- |Runs the model's calibrated (or constructor-supplied) alpha\/beta\/omega recursion forward
-- over a return series. The output series is offset by one from the input: the first input
-- point has nothing to forecast from, so it is dropped, and one extra point is extrapolated one
-- step past the input series' last date -- an @n@-point input still produces an @n@-point
-- output, just shifted forward by one date.
calculate :: Garch11 -> NonEmpty (Day, Double) -- ^return series
-> IO [(Day, Double)]
calculate g series = do
(ds, vs) <- qlGarch11Calculate g dates vals
return $ zip ds vs
where (dates, vals) = unzip (toList series)
{#fun qlGarch11Calculate{withGarch11*`Garch11',withDayArray*`[Day]'&,withDoubleArray*`[Double]'&
,preArray-`[Day]'&peekDayArray*,preArray-`[Double]'&peekDoubleArray*
,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |Garman-Klass (1980) simple estimator: @sigma^2 = ln(close\/open)^2@, scaled by @yearFraction@.
-- Input bars are @(date, open, close, high, low)@; only open\/close are used here.
garmanKlassSimpleSigma :: Double -- ^yearFraction
-> NonEmpty (Day, Double, Double, Double, Double) -- ^(date, open, close, high, low) price bars
-> IO [(Day, Double)]
garmanKlassSimpleSigma yearFraction bars = do
(ds, vs) <- qlGarmanKlassSimpleSigma yearFraction dates opens closes highs lows
return $ zip ds vs
where (dates, opens, closes, highs, lows) = unzipBars bars
{#fun qlGarmanKlassSimpleSigma{`Double',withDayArray*`[Day]'&,withDoubleArray*`[Double]'&
,withDoubleArray*`[Double]'&,withDoubleArray*`[Double]'&,withDoubleArray*`[Double]'&
,preArray-`[Day]'&peekDayArray*,preArray-`[Double]'&peekDoubleArray*
,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |Garman-Klass Sigma1: blends 'garmanKlassSimpleSigma' with the overnight (previous close to
-- today's open) jump, weighted by @marketOpenFraction@ (the fraction of the trading day the
-- market is open). Drops the series' first bar (needs a previous close).
garmanKlassSigma1 :: Double -- ^yearFraction
-> Double -- ^marketOpenFraction
-> NonEmpty (Day, Double, Double, Double, Double) -- ^(date, open, close, high, low) price bars
-> IO [(Day, Double)]
garmanKlassSigma1 yearFraction marketOpenFraction bars = do
(ds, vs) <- qlGarmanKlassSigma1 yearFraction marketOpenFraction dates opens closes highs lows
return $ zip ds vs
where (dates, opens, closes, highs, lows) = unzipBars bars
{#fun qlGarmanKlassSigma1{`Double',`Double',withDayArray*`[Day]'&,withDoubleArray*`[Double]'&
,withDoubleArray*`[Double]'&,withDoubleArray*`[Double]'&,withDoubleArray*`[Double]'&
,preArray-`[Day]'&peekDayArray*,preArray-`[Double]'&peekDoubleArray*
,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |Parkinson (1980) high-low estimator: @sigma^2 = ln(high\/low)^2 \/ (4 ln 2)@, scaled by
-- @yearFraction@. Input bars are @(date, open, close, high, low)@; only high\/low are used here.
parkinsonSigma :: Double -- ^yearFraction
-> NonEmpty (Day, Double, Double, Double, Double) -- ^(date, open, close, high, low) price bars
-> IO [(Day, Double)]
parkinsonSigma yearFraction bars = do
(ds, vs) <- qlParkinsonSigma yearFraction dates opens closes highs lows
return $ zip ds vs
where (dates, opens, closes, highs, lows) = unzipBars bars
{#fun qlParkinsonSigma{`Double',withDayArray*`[Day]'&,withDoubleArray*`[Double]'&
,withDoubleArray*`[Double]'&,withDoubleArray*`[Double]'&,withDoubleArray*`[Double]'&
,preArray-`[Day]'&peekDayArray*,preArray-`[Double]'&peekDoubleArray*
,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |Garman-Klass Sigma3: blends 'parkinsonSigma' with the overnight jump, same
-- @marketOpenFraction@ weighting as 'garmanKlassSigma1'. Drops the series' first bar.
garmanKlassSigma3 :: Double -- ^yearFraction
-> Double -- ^marketOpenFraction
-> NonEmpty (Day, Double, Double, Double, Double) -- ^(date, open, close, high, low) price bars
-> IO [(Day, Double)]
garmanKlassSigma3 yearFraction marketOpenFraction bars = do
(ds, vs) <- qlGarmanKlassSigma3 yearFraction marketOpenFraction dates opens closes highs lows
return $ zip ds vs
where (dates, opens, closes, highs, lows) = unzipBars bars
{#fun qlGarmanKlassSigma3{`Double',`Double',withDayArray*`[Day]'&,withDoubleArray*`[Double]'&
,withDoubleArray*`[Double]'&,withDoubleArray*`[Double]'&,withDoubleArray*`[Double]'&
,preArray-`[Day]'&peekDayArray*,preArray-`[Double]'&peekDoubleArray*
,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |Garman-Klass (1980) Sigma4 estimator, combining the high-low range with the close-open
-- return via their published coefficients. Input bars are @(date, open, close, high, low)@.
garmanKlassSigma4 :: Double -- ^yearFraction
-> NonEmpty (Day, Double, Double, Double, Double) -- ^(date, open, close, high, low) price bars
-> IO [(Day, Double)]
garmanKlassSigma4 yearFraction bars = do
(ds, vs) <- qlGarmanKlassSigma4 yearFraction dates opens closes highs lows
return $ zip ds vs
where (dates, opens, closes, highs, lows) = unzipBars bars
{#fun qlGarmanKlassSigma4{`Double',withDayArray*`[Day]'&,withDoubleArray*`[Double]'&
,withDoubleArray*`[Double]'&,withDoubleArray*`[Double]'&,withDoubleArray*`[Double]'&
,preArray-`[Day]'&peekDayArray*,preArray-`[Double]'&peekDoubleArray*
,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |Garman-Klass (1980) Sigma5 estimator: an alternative high-low\/close-open combination to
-- 'garmanKlassSigma4', with different published coefficients.
garmanKlassSigma5 :: Double -- ^yearFraction
-> NonEmpty (Day, Double, Double, Double, Double) -- ^(date, open, close, high, low) price bars
-> IO [(Day, Double)]
garmanKlassSigma5 yearFraction bars = do
(ds, vs) <- qlGarmanKlassSigma5 yearFraction dates opens closes highs lows
return $ zip ds vs
where (dates, opens, closes, highs, lows) = unzipBars bars
{#fun qlGarmanKlassSigma5{`Double',withDayArray*`[Day]'&,withDoubleArray*`[Double]'&
,withDoubleArray*`[Double]'&,withDoubleArray*`[Double]'&,withDoubleArray*`[Double]'&
,preArray-`[Day]'&peekDayArray*,preArray-`[Double]'&peekDoubleArray*
,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |Garman-Klass Sigma6: blends 'garmanKlassSigma5' with the overnight jump, same
-- @marketOpenFraction@ weighting as 'garmanKlassSigma1'. Drops the series' first bar.
garmanKlassSigma6 :: Double -- ^yearFraction
-> Double -- ^marketOpenFraction
-> NonEmpty (Day, Double, Double, Double, Double) -- ^(date, open, close, high, low) price bars
-> IO [(Day, Double)]
garmanKlassSigma6 yearFraction marketOpenFraction bars = do
(ds, vs) <- qlGarmanKlassSigma6 yearFraction marketOpenFraction dates opens closes highs lows
return $ zip ds vs
where (dates, opens, closes, highs, lows) = unzipBars bars
{#fun qlGarmanKlassSigma6{`Double',`Double',withDayArray*`[Day]'&,withDoubleArray*`[Double]'&
,withDoubleArray*`[Double]'&,withDoubleArray*`[Double]'&,withDoubleArray*`[Double]'&
,preArray-`[Day]'&peekDayArray*,preArray-`[Double]'&peekDoubleArray*
,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |Smooths an already-computed volatility series with a rolling constant estimate over the
-- trailing @windowSize@ points.
constantVolatilityEstimator :: Word -- ^windowSize
-> NonEmpty (Day, Double) -- ^volatility series
-> IO [(Day, Double)]
constantVolatilityEstimator windowSize series = do
(ds, vs) <- qlConstantVolatilityEstimator windowSize dates vals
return $ zip ds vs
where (dates, vals) = unzip (toList series)
{#fun qlConstantVolatilityEstimator{fromIntegral`Word',withDayArray*`[Day]'&,withDoubleArray*`[Double]'&
,preArray-`[Day]'&peekDayArray*,preArray-`[Double]'&peekDoubleArray*
,preErrorCheck-`String'errorCheck*-}->`()'#}
-- |Local (point-to-point) volatility estimate from a raw price series: @|ln(p_i\/p_{i-1})| \/
-- sqrt(yearFraction)@ for each consecutive pair. Drops the series' first date; an @n@-point
-- input produces an @(n-1)@-point output.
simpleLocalVolatilityEstimator :: Double -- ^yearFraction
-> NonEmpty (Day, Double) -- ^price series
-> IO [(Day, Double)]
simpleLocalVolatilityEstimator yearFraction series = do
(ds, vs) <- qlSimpleLocalVolatilityEstimator yearFraction dates vals
return $ zip ds vs
where (dates, vals) = unzip (toList series)
{#fun qlSimpleLocalVolatilityEstimator{`Double',withDayArray*`[Day]'&,withDoubleArray*`[Double]'&
,preArray-`[Day]'&peekDayArray*,preArray-`[Double]'&peekDoubleArray*
,preErrorCheck-`String'errorCheck*-}->`()'#}
unzipBars :: NonEmpty (Day, Double, Double, Double, Double) -> ([Day], [Double], [Double], [Double], [Double])
unzipBars bars = (map d5 xs, map o5 xs, map c5 xs, map h5 xs, map l5 xs)
where
xs = toList bars
d5 (d,_,_,_,_) = d
o5 (_,o,_,_,_) = o
c5 (_,_,c,_,_) = c
h5 (_,_,_,h,_) = h
l5 (_,_,_,_,l) = l
-- vim: set ff=unix ts=8 sts=2 sw=2 et: