| Safe Haskell | None |
|---|---|
| Language | Haskell2010 |
QuantLib.Method
Description
Monte Carlo path generation (pathGenerator/sobolPathGenerator/next/asset) plus
lsmRegress, a standalone Longstaff-Schwartz early-exercise regression primitive.
Custom early exercise with a Haskell payoff
lsmRegress lets a Haskell-defined payoff drive early exercise, something no bound pricing
engine offers: every early-exercise engine in QuantLib.PricingEngine (e.g.
mcAmericanEngine) computes its payoff entirely on the C++ side against a bound Payoff.
lsmRegress is pure regression -- it never sees a payoff at all, so it works for any
underlying-state-dependent early-exercise payoff, not just a vanilla put/call. The pattern,
worked in full in test/example/QuantLib/Example/AmericanLSM.hs:
- Draw two path sets with
pathGenerator: a calibration set used only to fit each date's regression, and a separate pricing set evaluated against the frozen fit. Splitting the sets avoids the in-sample bias a single-pass fit-and-price would have (the same reasonmcAmericanEngineexposes its ownnCalibrationSamplesparameter). Use a fixed nonzero seed for each -- seed0means "seed from entropy" forPseudoRandom. - Read out the state at every exercise date, across all paths, with
asset(orassetAt), and transpose (transpose) into one state list per exercise date. Walk exercise dates *strictly backward*. At each date:
- discount both the calibration and pricing cashflow vectors by the one-step discount
factor (
discount(t[i+1]) / discount(t[i])from the underlying yield curve); - compute the Haskell payoff at this date for every path in both sets;
- restrict the regression's fit inputs to *in-the-money calibration paths only*
(
fitStates/fitTargetsbelow); - call
lsmRegresstwice against that one fit -- once evaluating at the calibration states (to keep the backward recursion's own targets self-consistent), once at the pricing states (the actual out-of-sample continuation-value estimate); - exercise wherever
payoff > continuationValue(max(exercise, continuation)), on each path set independently.
- discount both the calibration and pricing cashflow vectors by the one-step discount
factor (
- The pricing set's cashflows, discounted all the way back and averaged, are the estimated price. Never evaluate the fit on the calibration set's own state for the reported price -- that reintroduces the in-sample bias step 1 split the paths to avoid.
A rough sketch (see the full example for discounting, ITM filtering, and the backward recursion itself):
step df calibS priceS calibCF priceCF = do
let calibTargets = map (* df) calibCF -- discount to this date
(fitStates, fitTargets) = -- ITM calibration paths only
unzip $ filter (inTheMoney . fst) $ zip calibS calibTargets
contCalib <- lsmRegress Monomial order fitStates fitTargets calibS
contPrice <- lsmRegress Monomial order fitStates fitTargets priceS
-- exercise wherever payoff > continuation, on each path set
...Validated in the example against both mcAmericanEngine pricing the equivalent bound vanilla
option (same process/grid/seed) and the published Longstaff-Schwartz (2001) reference value
for the same benchmark fixture.
The same pattern extends to a Haskell-defined basket payoff (several correlated underlyings)
via lsmRegressMulti / lsmBasisSize in place of lsmRegress: read each exercise date's state
for every underlying (still with asset/assetAt, once per underlying) into a Matrix of one
row per path, and guard the ITM-fit-size check with lsmBasisSize instead of the basis order --
the multi-asset basis has combinatorially many more terms than the scalar case.
test/example/QuantLib/Example/HaskellLSM.hs benchmarks lsmRegress against the same
backward induction with the per-date regression reimplemented from scratch in plain Haskell
(QuantLib used only for path generation) -- a worked illustration of why this module exposes
the regression as a batched primitive instead of leaving callers to reinvent it.
Finite-difference PDE solving
This module also binds a full-grid finite-difference (FDM) driver, built up across two related
issues (custom step-condition/operator hooks, then custom inner-value calculators) and spread
across several functions with no single overview until now. Worked in full in
test/example/QuantLib/Example/Fdm.hs -- every snippet below is a trimmed extract from that
file; read it end to end for the full picture (discounting, fixtures, imports).
In one sentence, for anyone new to FDM pricing: instead of an integral (Monte Carlo) or a
closed-form formula (an analytic*Engine), you discretize the underlying's state space (e.g.
log-spot) into a grid of points, put the option's payoff on the grid at maturity, and step it
backward to today, solving a small local linear system at each timestep. Reach for it when you
need American/Bermudan-style early exercise (a plain Monte Carlo run can't do backward induction
the way a grid can) or a process/payoff with no closed-form price. If a bound analytic*Engine
or mc*Engine already covers your case (see QuantLib.PricingEngine), prefer that instead --
it's simpler, and this module's own examples validate their FDM results against exactly those
engines.
Walkthrough: which function do I actually want?
Start here rather than at the reference list below -- picking the right entry point up front avoids reading five functions' haddock only to discover a sixth was the one you needed.
"I have a grid already, just roll it back"fdmRollback. Supply the grid as a plain[Double](one value per state, at maturity) plus three Haskell closures describing the PDE operator, and get the same grid rolled back to today -- no mesher, noFdmInnerValueCalculator, the simplest possible entry point:
let grid0 = map (\x -> max (exp x - strike) 0) xs -- payoff at maturity, one value per grid point fdmEuro <- fdmRollback 1 applyFn applyDirFn solveFn Nothing [] Douglas grid0 tMat 0 nSteps 0
"...and I need early exercise"- the same
fdmRollbackcall, plus a step condition: at -> [Double] -> [Double]closure called once per outer timestep with the whole current grid, returning it clamped to whatever the early-exercise rule requires:
let stepCond _t u = zipWith max u grid0 -- American: value can never fall below intrinsic fdmAmerican <- fdmRollback 1 applyFn applyDirFn solveFn (Just stepCond) stepTimes Douglas grid0 tMat 0 nSteps 0
"I'd rather not hand-build the initial grid myself"fdmSolve--fdmRollback's sibling. Same operator/step-condition/scheme machinery, but the initial condition comes from anFdmMesherplus anFdmInnerValueCalculatorevaluated at each node, instead of a grid you assembled by hand. Worth it once the mesher is doing real work (e.g. concentrating points near a strike or barrier) rather than just wrapping a list you already had:
mesh1d <- predefined1dMesher xs mesher <- fdmMesherComposite [mesh1d] let ivFn _t loc = case loc of [x] -> intrinsicAt x; _ -> error "expected a 1D location" withCustomFdmInnerValueCalculator mesher ivFn ivFn $ \calc -> fdmSolve mesher calc 1 applyFn applyDirFn solveFn Nothing [] Douglas tMat 0 nSteps 0
"my payoff is a standard vanilla/log payoff, I don't want a per-node callback"- skip
withCustomFdmInnerValueCalculatorand reach for one of the native calculators instead -- QuantLib's own built-inFdmInnerValueCalculatorsubclasses, bound directly so pricing a plain payoff doesn't pay a Haskell round-trip per grid node:
logCalc <- fdmLogInnerValue payoff mesher 0 -- striked payoff on a log-spot grid fdmLogEuro <- fdmSolve mesher logCalc 1 applyFn applyDirFn solveFn Nothing [] Douglas tMat 0 nSteps 0
fdmZeroInnerValue (always 0), fdmCellAveragingInnerValue/withCustomCellAveragingInnerValue
(identity or custom gridMapping), and fdmLogInnerValue (gridMapping = exp, the common case
on a log-spot grid) round out the set -- see the reference entries below for the exact
cell-averaging-vs-point-evaluation contract each one has. Reach for
withCustomFdmInnerValueCalculator only once none of these fit your payoff shape.
"my payoff depends on more than one underlying"- build one
Fdm1dMesherper underlying and combine them withfdmMesherComposite;fdmLogBasketInnerValuetakes a basket payoff (e.g.Max, see QuantLib.Instrument.Option) evaluated across all dimensions at once:
basketMesher <- fdmMesherComposite [mesh1d, mesh1d] -- two correlated log-spot dimensions basketCalc <- fdmLogBasketInnerValue (Max payoff) basketMesher val <- fdmAvgInnerValue basketCalc basketMesher [i, j] tMat -- inspect one node directly
"I want to price a swap/swaption under a calibrated short-rate model"- the most specialized
entry points here:
fdmAffineG2ModelSwapInnerValue/fdmAffineHullWhiteModelSwapInnerValuedrive the same calculatorfdG2SwaptionEngine/fdHullWhiteSwaptionEnginealready use internally. Reach for these directly only when composing your own custom FDM pipeline around this calculator; if a plain Bermudan-swaption NPV is all you need, prefer those two already-bound black-box engines from QuantLib.PricingEngine instead. "I just want one node's value, no PDE solve"fdmInnerValue/fdmAvgInnerValueevaluate any bound calculator -- custom or native -- at a single mesher node directly, without assembling a wholefdmSolve. Handy as a sanity check while developing (asFdm.hs's own tests do throughout), or whenever a single point's intrinsic value is all you actually need.
Technical reference
The terse version of the above, for a reader who already knows the vocabulary and wants the exact contract rather than the walkthrough's prose.
Rolling a grid backfdmRollbacktakes a precomputed initial grid (a plain[Double]) and rolls it back through time via three Haskell-defined operator callbacks (withFdmApplyet al.) plus an optional step condition (e.g. American/Bermudan early exercise). These callbacks cross the language boundary once per outer timestep, over the whole grid.fdmSolveis the sibling that instead derives its own initial grid from a mesher and anFdmInnerValueCalculator(below), reusing the same operator/step-condition machinery.Building a gridFdm1dMeshers (predefined1dMesher,uniform1dMesher,concentrating1dMesher,fdmBlackScholesMesher, and the other process-specific meshers) each describe one PDE dimension;fdmMesherCompositecombines one or more into the multi-dimensionalFdmMesherfdmSolveandFdmInnerValueCalculatoroperate over.fdmMesherLocationsreads a dimension's real-valued node locations back out, e.g. to map a flat result array back to coordinates.gluedMeshersplices twoFdm1dMeshers end to end (e.g. a fine mesh near a barrier glued to a coarse one further out) -- their ranges must already be ordered and non-overlapping, and a shared boundary point is deduplicated automatically.Custom inner values, fully generalwithCustomFdmInnerValueCalculatorwraps a Haskellt -> location -> valuepair of functions as anFdmInnerValueCalculator. Unlike every callback above, this one crosses the language boundary once per grid node -- there is no batched shape for it anywhere in QuantLib or QuantLib-SWIG, so the real per-call cost is accepted, matching QuantLib-SWIG's ownFdmInnerValueCalculatorDelegateprecedent. Because the two callbacks are stored inside the returned calculator and invoked again on every laterfdmSolve/fdmInnerValuecall (not just during construction), the calculator is only valid inside this continuation -- it cannot be built with a plainIO FdmInnerValueCalculatorsmart constructor the way the native calculators below can.Custom inner values, native- QuantLib's own concrete
FdmInnerValueCalculatorsubclasses are bound directly, for the common cases that don't need a per-node Haskell callback at all:fdmZeroInnerValue(always 0),fdmCellAveragingInnerValue/fdmLogInnerValue(a payoff cell-averaged -- Simpson-integrated across each grid cell, not just evaluated at its center -- with an identity orexpvalue mapping respectively), andfdmLogBasketInnerValue(the multi-asset counterpart, oneexpmapping per dimension). These hold no Haskell callback, so they're plainIO FdmInnerValueCalculatorconstructors -- exceptwithCustomCellAveragingInnerValue, the one native constructor that does take an explicitgridMappingcallback, which needs the same continuation treatment as the fully custom case above.fdmAffineG2ModelSwapInnerValue/fdmAffineHullWhiteModelSwapInnerValueprice a swap under a calibratedG2/HullWhitemodel directly -- the same calculatorfdG2SwaptionEngine/fdHullWhiteSwaptionEngineuse internally. Inspecting a calculator directlyfdmInnerValue/fdmAvgInnerValueevaluate any bound calculator (custom or native) at a single mesher node, without assembling a wholefdmSolve-- useful for a targeted self-consistency check, asFdm.hs's own tests do throughout.
What's deliberately not bound: operators, schemes, boundary conditions
QuantLib-SWIG also exposes QuantLib's concrete FdmLinearOpComposite subclasses (FdmBlackScholesOp,
FdmHestonOp, FdmG2Op, ...), its scheme objects (DouglasScheme, CraigSneydScheme,
HundsdorferScheme, ...), and its FdmBoundaryCondition family as real C++ objects. hasquant does
not mirror these, and won't by default -- it's a design boundary already crossed once, not a gap.
fdmRollback/fdmSolve take the operator, the implicit-solve step, and the scheme all as Haskell
closures instead (applyFn/applyDirFn/solveFn above). That's the same "coarsen the
language-boundary crossing" call already made for step conditions: bind the reusable numerical
primitive (rollback through a fixed timestep, of an arbitrary tridiagonal/multi-dimensional
operator) and let Haskell drive it, rather than bind every concrete operator/scheme QuantLib ships
as its own object. test/example/QuantLib/Example/Fdm.hs's hand-rolled operatorBands/applyOp
is the replacement for FdmBlackScholesOp + DouglasScheme, not a stand-in waiting for those to
get bound -- pricing a new payoff/process combination here means writing its operator once in
Haskell, not calling into fifteen QuantLib operator classes one by one.
Binding the operator/scheme family as objects would add a second, redundant way to drive the same
fdmSolve/fdmRollback backbone, without extending what's actually solvable -- anything a bound
FdmXxxOp could do, a Haskell applyFn already can. Revisit only if a concrete need shows up that
the callback shape genuinely can't express (none has, so far).
Synopsis
- data PathGenerator
- data SamplePath
- data GaussianRsg
- data Fdm1dMesher
- data FdmMesher
- data FdmInnerValueCalculator
- pathGenerator :: RngTrait -> GenStochasticProcess p -> TimeGrid -> Word -> Word -> Bool -> IO PathGenerator
- sobolPathGenerator :: SobolDirectionIntegers -> GenStochasticProcess p -> TimeGrid -> Word -> Word -> Bool -> IO PathGenerator
- gaussianRsg :: RngTrait -> Word -> Word -> IO GaussianRsg
- sobolGaussianRsg :: SobolDirectionIntegers -> Word -> Word -> IO GaussianRsg
- predefined1dMesher :: RealVector -> IO Fdm1dMesher
- uniform1dMesher :: Double -> Double -> Word -> IO Fdm1dMesher
- concentrating1dMesher :: Double -> Double -> Word -> Maybe Double -> Maybe Double -> Bool -> IO Fdm1dMesher
- concentrating1dMesherMulti :: Double -> Double -> Word -> [(Double, Double, Bool)] -> Double -> IO Fdm1dMesher
- gluedMesher :: Fdm1dMesher -> Fdm1dMesher -> IO Fdm1dMesher
- fdmBlackScholesMesher :: Word -> GeneralizedBlackScholesProcess -> Double -> Double -> Maybe Double -> Maybe Double -> Double -> Double -> Maybe Double -> Maybe Double -> [Dividend] -> Maybe FdmQuantoHelper -> Double -> IO Fdm1dMesher
- fdmCev1dMesher :: Word -> Double -> Double -> Double -> Double -> Double -> Double -> Maybe Double -> Maybe Double -> IO Fdm1dMesher
- exponentialJump1dMesher :: Word -> Double -> Double -> Double -> Double -> IO Fdm1dMesher
- fdmSimpleProcess1dMesher :: Word -> StochasticProcess1D -> Double -> Word -> Double -> Maybe Double -> IO Fdm1dMesher
- fdmHestonVarianceMesher :: Word -> GenHestonProcess hp -> Double -> Word -> Double -> Double -> IO Fdm1dMesher
- fdmHestonLocalVolatilityVarianceMesher :: Word -> GenHestonProcess hp -> GenLocalVolTermStructure lv -> Double -> Word -> Double -> Double -> IO Fdm1dMesher
- fdmMesherComposite :: [Fdm1dMesher] -> IO FdmMesher
- withCustomFdmInnerValueCalculator :: FdmMesher -> (Double -> [Double] -> Double) -> (Double -> [Double] -> Double) -> (FdmInnerValueCalculator -> IO b) -> IO b
- fdmZeroInnerValue :: IO FdmInnerValueCalculator
- fdmCellAveragingInnerValue :: Payoff -> FdmMesher -> Int -> IO FdmInnerValueCalculator
- withCustomCellAveragingInnerValue :: Payoff -> FdmMesher -> Int -> (Double -> Double) -> (FdmInnerValueCalculator -> IO b) -> IO b
- fdmLogInnerValue :: Payoff -> FdmMesher -> Int -> IO FdmInnerValueCalculator
- fdmLogBasketInnerValue :: BasketPayoff -> FdmMesher -> IO FdmInnerValueCalculator
- fdmAffineG2ModelSwapInnerValue :: G2 -> G2 -> GenFixedVsFloatingSwap f -> [(Double, Day)] -> FdmMesher -> Int -> IO FdmInnerValueCalculator
- fdmAffineHullWhiteModelSwapInnerValue :: HullWhite -> HullWhite -> GenFixedVsFloatingSwap f -> [(Double, Day)] -> FdmMesher -> Int -> IO FdmInnerValueCalculator
- next :: PathGenerator -> IO SamplePath
- antithetic :: PathGenerator -> IO SamplePath
- nextSequence :: GaussianRsg -> IO (RealVector, Double)
- lsmRegress :: PolynomialType -> Word -> RealVector -> RealVector -> RealVector -> IO RealVector
- lsmBasisSize :: Word -> Word -> Word
- lsmRegressMulti :: PolynomialType -> Word -> RealMatrix -> RealVector -> RealMatrix -> IO RealVector
- fdmRollback :: Int -> ((Double, Double) -> RealVector -> RealVector) -> (Int -> (Double, Double) -> RealVector -> RealVector) -> (Int -> Double -> (Double, Double) -> RealVector -> RealVector) -> Maybe (Double -> RealVector -> RealVector) -> RealVector -> FdmScheme -> RealVector -> Double -> Double -> Int -> Int -> IO RealVector
- fdmInnerValue :: FdmInnerValueCalculator -> FdmMesher -> [Int] -> Double -> IO Double
- fdmAvgInnerValue :: FdmInnerValueCalculator -> FdmMesher -> [Int] -> Double -> IO Double
- fdmSolve :: FdmMesher -> FdmInnerValueCalculator -> Int -> ((Double, Double) -> RealVector -> RealVector) -> (Int -> (Double, Double) -> RealVector -> RealVector) -> (Int -> Double -> (Double, Double) -> RealVector -> RealVector) -> Maybe (Double -> RealVector -> RealVector) -> RealVector -> FdmScheme -> Double -> Double -> Int -> Int -> IO RealVector
- weight :: SamplePath -> Double
- assetNumber :: SamplePath -> Word
- pathSize :: SamplePath -> Word
- assetAt :: SamplePath -> Word -> Word -> IO Double
- asset :: SamplePath -> Word -> IO RealVector
- rsgDimension :: GaussianRsg -> Word
- lastSequence :: GaussianRsg -> IO (RealVector, Double)
- fdmMesherLocations :: FdmMesher -> Int -> IO RealVector
Types
Path and random sequences
data PathGenerator Source #
data SamplePath Source #
data GaussianRsg Source #
Finite differences
data Fdm1dMesher Source #
Constructors
Path generation
Arguments
| :: RngTrait | |
| -> GenStochasticProcess p | |
| -> TimeGrid | |
| -> Word | seed |
| -> Word | dimension |
| -> Bool | brownian bridge |
| -> IO PathGenerator |
build a multi-asset path generator driven by a pseudo-random number generator (Mersenne Twister, Poisson, or Ziggurat, chosen by the RNG trait) over the given process and time grid.
Arguments
| :: SobolDirectionIntegers | |
| -> GenStochasticProcess p | |
| -> TimeGrid | |
| -> Word | seed |
| -> Word | dimension |
| -> Bool | brownian bridge |
| -> IO PathGenerator |
build a multi-asset path generator driven by a low-discrepancy (Sobol) sequence, using the given direction integers, over the given process and time grid.
Random sequence generation
Arguments
| :: RngTrait | |
| -> Word | dimension |
| -> Word | seed |
| -> IO GaussianRsg |
The gaussian sequence generator a pathGenerator drives its evolution with, exposed on its own
so a Haskell-defined SDE can be simulated with no FFI call in the inner loop -- the same
decomposition lsmRegress applies to LongstaffSchwartzPathPricer, one level lower down.
QuantLib's StochasticProcess has no Haskell-subclassable hook here by design: MultiPathGenerator
(which pathGenerator wraps) calls process->evolve once per timestep per path, so binding
that virtual as a callback would put an FFI crossing in the hottest loop there is. Drawing the
normals with nextSequence and writing evolve in Haskell instead costs one crossing per
path, and the result composes with lsmRegress into a complete custom-SDE American Monte
Carlo. The trade-off is that the result is a set of paths, not a StochasticProcess object, so
it cannot be fed to fdmSimpleProcess1dMesher or to a pricing engine -- but no stock QuantLib
engine would have accepted a custom process anyway: their constructors are typed on concrete
process classes (GeneralizedBlackScholesProcess and friends), not on the abstract base.
dimension is the length of each drawn sequence -- for a path set, assets * timesteps,
matching what pathGenerator is passed. The construction mirrors pathGenerator's exactly
(same trait, same seed, same direction integers), so a Haskell-evolved path can be compared
draw for draw against a pathGenerator one on a bound process.
Arguments
| :: SobolDirectionIntegers | |
| -> Word | dimension |
| -> Word | seed |
| -> IO GaussianRsg |
gaussianRsg driven by a low-discrepancy (Sobol) sequence with the given direction integers --
the sobolPathGenerator counterpart.
Finite-difference meshers
Arguments
| :: RealVector | points |
| -> IO Fdm1dMesher |
'Predefined1dMesher(points)' -- an Fdm1dMesher over an explicit, caller-supplied set of grid points.
Arguments
| :: Double | start |
| -> Double | end |
| -> Word | size |
| -> IO Fdm1dMesher |
'Uniform1dMesher(start, end, size)' -- an evenly spaced Fdm1dMesher.
concentrating1dMesher Source #
Arguments
| :: Double | start |
| -> Double | end |
| -> Word | size |
| -> Maybe Double | concentration point location |
| -> Maybe Double | concentration point density |
| -> Bool | requireCPoint: force the concentration point itself onto the grid |
| -> IO Fdm1dMesher |
'Concentrating1dMesher(start, end, size, cPoint, requireCPoint)' -- an Fdm1dMesher with grid
points concentrated near cPoint (e.g. a strike or barrier), or plain uniform spacing when
cPoint is Nothing for both coordinates.
concentrating1dMesherMulti Source #
Arguments
| :: Double | |
| -> Double | |
| -> Word | |
| -> [(Double, Double, Bool)] | concentration points: (location, density, requireCPoint) |
| -> Double | tol |
| -> IO Fdm1dMesher |
Multi-concentration-point overload of concentrating1dMesher
(Concentrating1dMesher(start, end, size, cPoints, tol)) -- a distinct upstream constructor,
not a defaulted-arg variant of the single-point one.
Arguments
| :: Fdm1dMesher | leftMesher |
| -> Fdm1dMesher | rightMesher |
| -> IO Fdm1dMesher |
'Glued1dMesher(leftMesher, rightMesher)' -- splices two Fdm1dMeshers into one, deduplicating
their shared boundary point if leftMesher's rightmost location and rightMesher's leftmost
location coincide (within QuantLib's usual close tolerance). Throws if leftMesher's rightmost
point is strictly greater than rightMesher's leftmost point -- the two ranges may touch or be
disjoint-but-ordered, never overlap or reverse.
fdmBlackScholesMesher Source #
Arguments
| :: Word | size |
| -> GeneralizedBlackScholesProcess | |
| -> Double | maturity |
| -> Double | strike |
| -> Maybe Double | xMinConstraint |
| -> Maybe Double | xMaxConstraint |
| -> Double | eps |
| -> Double | scaleFactor |
| -> Maybe Double | concentration point location |
| -> Maybe Double | concentration point density |
| -> [Dividend] | |
| -> Maybe FdmQuantoHelper | |
| -> Double | spotAdjustment |
| -> IO Fdm1dMesher |
'FdmBlackScholesMesher(size, process, maturity, strike, ...)' -- the standard log-spot mesher
for a Black-Scholes-family process, reusing the same GeneralizedBlackScholesProcess/
Dividend/FdmQuantoHelper plumbing QuantLib.PricingEngine's fd* engines already use.
Arguments
| :: Word | size |
| -> Double | f0 |
| -> Double | alpha |
| -> Double | beta |
| -> Double | maturity |
| -> Double | eps |
| -> Double | scaleFactor |
| -> Maybe Double | concentration point location |
| -> Maybe Double | concentration point density |
| -> IO Fdm1dMesher |
'FdmCEV1dMesher(size, f0, alpha, beta, maturity, eps, scaleFactor, cPoint)' -- the standard mesher for a CEV process.
exponentialJump1dMesher Source #
'ExponentialJump1dMesher(steps, beta, jumpIntensity, eta, eps)' -- mesher for the jump-diffusion component of a jump-diffusion process.
fdmSimpleProcess1dMesher Source #
Arguments
| :: Word | size |
| -> StochasticProcess1D | |
| -> Double | maturity |
| -> Word | tAvgSteps |
| -> Double | epsilon |
| -> Maybe Double | mandatoryPoint |
| -> IO Fdm1dMesher |
'FdmSimpleProcess1dMesher(size, process, maturity, tAvgSteps, epsilon, mandatoryPoint)' --
generic mesher for any bound one-dimensional StochasticProcess1D.
fdmHestonVarianceMesher Source #
Arguments
| :: Word | size |
| -> GenHestonProcess hp | |
| -> Double | maturity |
| -> Word | tAvgSteps |
| -> Double | epsilon |
| -> Double | mixingFactor |
| -> IO Fdm1dMesher |
'FdmHestonVarianceMesher(size, process, maturity, tAvgSteps, epsilon, mixingFactor)' -- variance mesher for a Heston-family process.
fdmHestonLocalVolatilityVarianceMesher Source #
Arguments
| :: Word | size |
| -> GenHestonProcess hp | |
| -> GenLocalVolTermStructure lv | leverageFct |
| -> Double | maturity |
| -> Word | tAvgSteps |
| -> Double | epsilon |
| -> Double | mixingFactor |
| -> IO Fdm1dMesher |
'FdmHestonLocalVolatilityVarianceMesher(size, process, leverageFct, maturity, tAvgSteps, epsilon, mixingFactor)' -- Heston variance mesher accounting for a local-volatility leverage function.
fdmMesherComposite :: [Fdm1dMesher] -> IO FdmMesher Source #
FdmMesherComposite -- combine one or more Fdm1dMeshers into the multi-dimensional
FdmMesher the operator/step-condition callbacks and fdmSolve operate over; the sole
concrete FdmMesher upstream.
Finite-difference inner-value calculators
withCustomFdmInnerValueCalculator Source #
Arguments
| :: FdmMesher | |
| -> (Double -> [Double] -> Double) | innerValue(t, location) |
| -> (Double -> [Double] -> Double) | avgInnerValue(t, location) |
| -> (FdmInnerValueCalculator -> IO b) | |
| -> IO b |
Wraps a Haskell t -> location -> value pair of innerValue/avgInnerValue functions as a
real FdmInnerValueCalculator object, valid only inside the continuation -- the fully custom
counterpart to constructors built from QuantLib's own concrete subclasses (bound alongside
this, which need no such bracket: they hold no Haskell callback). Unlike every callback
fdmRollback takes, this crosses the language boundary once per grid node, not once per outer
iteration over the whole grid -- there is no batched "whole-grid inner value" shape anywhere
in QuantLib or QuantLib-SWIG. The per-call FFI cost across
every node (and, if a step condition also calls the calculator, every node at every exercise
date) is accepted -- matching QuantLib-SWIG's own accepted-cost precedent,
FdmInnerValueCalculatorDelegate (SWIG/fdm.i).
fdmZeroInnerValue :: IO FdmInnerValueCalculator Source #
FdmZeroInnerValue -- an FdmInnerValueCalculator whose innerValue/avgInnerValue are
always 0.
fdmCellAveragingInnerValue Source #
Arguments
| :: Payoff | |
| -> FdmMesher | |
| -> Int | direction |
| -> IO FdmInnerValueCalculator |
'FdmCellAveragingInnerValue(payoff, mesher, direction)' -- cell-averages payoff over each
grid cell along direction (Simpson-integrating across the cell straddling a kink, e.g. a
strike, rather than just evaluating at the cell center), with the identity value mapping. See
withCustomCellAveragingInnerValue for the gridMapping-taking overload (e.g. to reproduce
fdmLogInnerValue by hand), and fdmLogInnerValue for the common log-mapped case QuantLib
itself gives its own dedicated subclass.
withCustomCellAveragingInnerValue :: Payoff -> FdmMesher -> Int -> (Double -> Double) -> (FdmInnerValueCalculator -> IO b) -> IO b Source #
As fdmCellAveragingInnerValue, but with an explicit gridMapping :: Double -> Double applied
to each node's location before the payoff sees it (e.g. exp on a log-spot grid, reproducing
fdmLogInnerValue by hand) -- a genuine per-node Haskell callback; see
withCustomFdmInnerValueCalculator. The
resulting FdmInnerValueCalculator is only valid inside this continuation.
Arguments
| :: Payoff | |
| -> FdmMesher | |
| -> Int | direction |
| -> IO FdmInnerValueCalculator |
'FdmLogInnerValue(payoff, mesher, direction)' -- fdmCellAveragingInnerValue with the
gridMapping = exp QuantLib itself gives its own dedicated subclass (the standard shape for a
log-spot grid, e.g. fdmBlackScholesMesher's own grid).
fdmLogBasketInnerValue :: BasketPayoff -> FdmMesher -> IO FdmInnerValueCalculator Source #
'FdmLogBasketInnerValue(payoff, mesher)' -- the multi-asset counterpart to fdmLogInnerValue:
evaluates a BasketPayoff with each dimension's location exponentiated first (exp on every
mesher direction, i.e. a log-spot grid per underlying), no cell averaging.
fdmAffineG2ModelSwapInnerValue :: G2 -> G2 -> GenFixedVsFloatingSwap f -> [(Double, Day)] -> FdmMesher -> Int -> IO FdmInnerValueCalculator Source #
'FdmAffineModelSwapInnerValue<G2>(disModel, fwdModel, swap, exerciseDates, mesher, direction)'
-- the swap-NPV-under-the-model FdmInnerValueCalculator used internally by
fdG2SwaptionEngine. exerciseDates pairs each exercise time (the same
Time-as-Double year-fraction convention used throughout, not a dedicated type) with the
Day it corresponds to (upstream's std::map<Time, Date>).
fdmAffineHullWhiteModelSwapInnerValue :: HullWhite -> HullWhite -> GenFixedVsFloatingSwap f -> [(Double, Day)] -> FdmMesher -> Int -> IO FdmInnerValueCalculator Source #
As fdmAffineG2ModelSwapInnerValue, but for HullWhite -- used internally by
fdHullWhiteSwaptionEngine.
Inspectors
Path and random sequences
next :: PathGenerator -> IO SamplePath Source #
draw the next weighted sample path from the generator.
antithetic :: PathGenerator -> IO SamplePath Source #
draw the antithetic (sign-flipped) counterpart of the last drawn sample path.
nextSequence :: GaussianRsg -> IO (RealVector, Double) Source #
draw the next sequence of standard normal variates, with its sample weight (1 for every trait
bound here, carried through for symmetry with weight).
Longstaff-Schwartz regression
Arguments
| :: PolynomialType | |
| -> Word | basis order |
| -> RealVector | fit states (in-the-money paths only) |
| -> RealVector | fit targets (continuation value at these states) |
| -> RealVector | eval states (all paths' state at this date) |
| -> IO RealVector |
one step of Longstaff-Schwartz early-exercise regression: fit a polynomial basis of the given
order/type against the (in-the-money) fit states and their continuation targets, then evaluate the
fitted continuation value at each of the given eval states. This is the same per-exercise-date
regression LongstaffSchwartzPathPricer performs internally against a bound Payoff, exposed so it
can be driven from a Haskell-defined payoff instead: call it once per exercise date, walking dates
strictly backward, batched across all paths rather than per path. See this module's header for the
full backward-induction pattern.
lsmBasisSize :: Word -> Word -> Word Source #
number of basis terms lsmRegressMulti fits for a given number of underlyings and order --
C(dim+order, order), the binomial coefficient LsmBasisSystem::multiPathBasisSystem actually
returns (not order+1, which only coincides at dim=1 -- lsmRegress uses that special case
directly rather than calling this). Use it to size the "enough in-the-money calibration paths to
fit" guard before calling lsmRegressMulti: the underlying least-squares solve requires at least
this many fit rows, and undershooting it throws rather than returning a degenerate fit.
Arguments
| :: PolynomialType | |
| -> Word | |
| -> RealMatrix | fit states (in-the-money paths only) |
| -> RealVector | fit targets (continuation value at these states) |
| -> RealMatrix | eval states (all paths' state at this date) |
| -> IO RealVector | continuation value estimate per eval row |
multi-asset counterpart of lsmRegress, for a Haskell-defined basket (several correlated
underlyings) early-exercise payoff -- lsmRegress itself only regresses against one state
variable. Fit/eval states are contiguous row-major RealMatrix values: one row per path, one column per underlying, and the
two matrices' column counts must agree. Regresses against
LsmBasisSystem::multiPathBasisSystem's combinatorial basis; see lsmBasisSize for its size and
this module's header for the surrounding backward-induction pattern (identical to the scalar case,
just with Matrix-shaped states).
Finite differences
Arguments
| :: Int | number of PDE directions/dimensions the operator has (e.g. 1 for a 1D Black-Scholes-in-log-spot operator) -- not the grid array length, which is the length of every |
| -> ((Double, Double) -> RealVector -> RealVector) |
|
| -> (Int -> (Double, Double) -> RealVector -> RealVector) | apply_direction(direction, r) |
| -> (Int -> Double -> (Double, Double) -> RealVector -> RealVector) |
|
| -> Maybe (Double -> RealVector -> RealVector) | optional step condition |
| -> RealVector | stopping times at which the step condition above is applied (ignored if there is no step condition); pass every rollback step's time to apply it at every step |
| -> FdmScheme | the finite-difference scheme (see the haddock above for which schemes are actually safe to use here) |
| -> RealVector | initial grid values, at time 'from' |
| -> Double | from (start time of the rollback, e.g. option maturity) |
| -> Double | to (end time of the rollback, e.g. 0) |
| -> Int | steps |
| -> Int | dampingSteps |
| -> IO RealVector |
Drive FdmBackwardSolver::rollback with a Haskell-defined FdmLinearOpComposite (the
apply/apply_direction/solve_splitting callbacks) and an optional Haskell-defined step
condition (e.g. American/Bermudan early exercise, or a barrier), instead of a bound mesher +
FdmInnerValueCalculator as every concrete FDM pricing engine in QuantLib.PricingEngine
uses. This coarsened callback shape is modeled on QuantLib-SWIG's
FdmLinearOpCompositeDelegate/
FdmStepConditionDelegate (SWIG/fdm.i): each callback crosses once per outer iteration over
the whole grid array, not once per grid node.
Internally each callback receives one native argument record, avoiding a wide mixed-argument
callback ABI at the Haskell boundary; this does not affect the public callback types.
The grid is a plain [Double] in and out -- no mesher, no FdmInnerValueCalculator, no
FdmSolverDesc is bound; callers manage their own grid geometry entirely in Haskell. Boundary
conditions are always the empty FdmBoundaryConditionSet() (not bound).
Only DouglasScheme::step's three virtuals are implemented -- apply, apply_direction and
solve_splitting; apply_mixed/preconditioner are unimplemented and QL_FAIL at the C++
level if called. This makes fdmRollback safe to drive with Douglas
or CrankNicolson in one dimension (the two schemes
DouglasScheme::step itself is used for) -- anything needing mixed derivatives across more than
one PDE direction (Craig-Sneyd, Hundsdorfer, or any genuinely multi-dimensional operator) will
throw partway through fdmRollback rather than silently mispricing.
Evaluate an FdmInnerValueCalculator's innerValue at the mesher node given by its
coordinates (one index per PDE dimension), at time t -- lets any bound calculator (native or
built via withCustomFdmInnerValueCalculator) be inspected directly without assembling a whole
fdmSolve.
As fdmInnerValue, but for avgInnerValue.
Arguments
| :: FdmMesher | |
| -> FdmInnerValueCalculator | |
| -> Int | number of PDE directions/dimensions the operator has |
| -> ((Double, Double) -> RealVector -> RealVector) | apply(r) |
| -> (Int -> (Double, Double) -> RealVector -> RealVector) | apply_direction(direction, r) |
| -> (Int -> Double -> (Double, Double) -> RealVector -> RealVector) | solve_splitting(direction, r, s) |
| -> Maybe (Double -> RealVector -> RealVector) | optional step condition |
| -> RealVector | stopping times at which the step condition above is applied |
| -> FdmScheme | the finite-difference scheme |
| -> Double | maturity (start time of the rollback, and the time at which avgInnerValue builds the initial grid) |
| -> Double | to (end time of the rollback, e.g. 0) |
| -> Int | steps |
| -> Int | dampingSteps |
| -> IO RealVector |
Sibling of fdmRollback that derives its own initial grid from a mesher and an
FdmInnerValueCalculator (avgInnerValue(t, location) per node, called once per mesher node at
t = maturity -- mirroring Fdm1DimSolver/FdmNdimSolver's own constructor loop) instead of
taking a precomputed grid array. Everything else (operator/step-condition/scheme/rollback) is
identical to fdmRollback, reusing the same callback machinery. The calculator can be either
fully custom (fdmInnerValueCalculator) or one of QuantLib's own native subclasses.
Fdm1DimSolver/FdmNdimSolver themselves (their own LazyObject caching and cubic-spline
interpolation) are not bound; combine this function's result with fdmMesherLocations for
interpolation.
Paths
weight :: SamplePath -> Double Source #
the weight associated with a sample path.
assetNumber :: SamplePath -> Word Source #
the number of correlated asset paths in a sample.
pathSize :: SamplePath -> Word Source #
the number of time steps in each asset path of a sample.
Arguments
| :: SamplePath | |
| -> Word | asset |
| -> Word | point |
| -> IO Double |
the value of one asset's path at a given time step.
asset :: SamplePath -> Word -> IO RealVector Source #
The full simulated path (values at every time step) of a single asset.
Random sequences and meshers
rsgDimension :: GaussianRsg -> Word Source #
the length of each sequence the generator draws.
lastSequence :: GaussianRsg -> IO (RealVector, Double) Source #
re-read the sequence nextSequence last drew, without advancing the generator.
Arguments
| :: FdmMesher | |
| -> Int | direction |
| -> IO RealVector |
Real-valued node locations along one dimension of a mesher, e.g. to map fdmSolve's flat
result array back to coordinates (mirrors how Fdm1DimSolver/FdmNdimSolver build their own
x_ arrays from this same call upstream).