net.finmath.montecarlo.interestrate.LIBORMarketModel Maven / Gradle / Ivy
/* * (c) Copyright Christian P. Fries, Germany. Contact: [email protected]. * * Created on 09.02.2004 */ package net.finmath.montecarlo.interestrate; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import net.finmath.exception.CalculationException; import net.finmath.functions.AnalyticFormulas; import net.finmath.marketdata.model.AnalyticModelInterface; import net.finmath.marketdata.model.curves.DiscountCurveFromForwardCurve; import net.finmath.marketdata.model.curves.DiscountCurveInterface; import net.finmath.marketdata.model.curves.ForwardCurveInterface; import net.finmath.marketdata.model.volatilities.AbstractSwaptionMarketData; import net.finmath.marketdata.products.Swap; import net.finmath.marketdata.products.SwapAnnuity; import net.finmath.montecarlo.AbstractRandomVariableFactory; import net.finmath.montecarlo.RandomVariableFactory; import net.finmath.montecarlo.interestrate.modelplugins.AbstractLIBORCovarianceModel; import net.finmath.montecarlo.interestrate.modelplugins.AbstractLIBORCovarianceModelParametric; import net.finmath.montecarlo.interestrate.products.AbstractLIBORMonteCarloProduct; import net.finmath.montecarlo.interestrate.products.SwaptionAnalyticApproximation; import net.finmath.montecarlo.interestrate.products.SwaptionSimple; import net.finmath.montecarlo.model.AbstractModel; import net.finmath.montecarlo.process.AbstractProcessInterface; import net.finmath.stochastic.RandomVariableInterface; import net.finmath.time.RegularSchedule; import net.finmath.time.ScheduleInterface; import net.finmath.time.TimeDiscretization; import net.finmath.time.TimeDiscretizationInterface; /** * Implements a (generalized) LIBOR market model with some drift approximation methods. *
or
* In its default case the class specifies a multi-factor LIBOR market model in its log-normal formulation, that is * Lj = exp(Yj) where * \[ * dY_{j} = \mu_{j} dt + \lambda_{1,j} dW_{1} + \ldots + \lambda_{m,j} dW_{m} * \] *
* The model uses anAbstractLIBORCovarianceModel
for the specification of * (λ1,j,...,λm,j) as a covariance model. * See {@link net.finmath.montecarlo.model.AbstractModelInterface} for details on the implemented interface *
* However, the class is more general: **
* *- * The model may be log-normal or normal specification with a given local volatility. *
*- * The class implements different measure(drift) / numeraire pairs: terminal measure and spot measure. *
*- * The class allows to configure a discounting curve (e.g. for "OIS discounting") using a simple deterministic zero spread. * In this case, the numeraire \( N(t) \) is adjusted by \( \exp( \int_0^t -\lambda(\tau) d\tau ) \). *
*
* The class specifies a LIBOR market model, that is * Lj = f(Yj) where **
* and *- * f is f(x) = exp(x) (default, log-normal LIBOR Market Model) or *
*- * f is f(x) = x (normal model, used if
*property.set("stateSpace","NORMAL"))
*
* dYj = μj dt + λ1,j dW1 + ... + λm,j dWm
*
* see {@link net.finmath.montecarlo.model.AbstractModelInterface} for details on the implemented interface. *
* The model uses anAbstractLIBORCovarianceModel
as a covariance model. * If the covariance model is of typeAbstractLIBORCovarianceModelParametric
* a calibration to swaptions can be performed. *
* Note that λ may still depend on L, hence generating a log-normal dynamic for L even * if the stateSpace property has been set to NORMAL. *
* * The mapproperties
allows to configure the model. The following keys may be used: **
*- *
*measure
: Possible values: **
*- *
*SPOT
: Simulate under spot measure. In this case, the single curve numeraire * is \( N(T_{i}) = \prod_{j=0}^{i-1} (1 + L(T_{j},T_{j+1};T_{j}) (T_{j+1}-T_{j})) \). *- *
*TERMINAL
: Simulate under terminal measure. In this case, the single curve numeraire * is \( N(T_{i}) = P(T_{n};T_{i}) = \prod_{j=i}^{n-1} (1 + L(T_{j},T_{j+1};T_{i}) (T_{j+1}-T_{j}))^{-1} \). *- *
*stateSpace
: Possible values: **
*- *
*LOGNORMAL
: The state space transform is set to exp, i.e., L = exp(Y). When the covariance model is deterministic, then this is the classical lognormal LIBOR market model. Note that the covariance model may still provide a local volatility function. *- *
*NORMAL
: The state space transform is set to identity, i.e., L = Y. When the covariance model is deterministic, then this is a normal LIBOR market model. Note that the covariance model may still provide a local volatility function. *- *
*liborCap
: An optionalDouble
value applied as a cap to the LIBOR rates. * May be used to limit the simulated valued to prevent values attaining POSITIVE_INFINITY and * numerical problems. To disable the cap, setliborCap
toDouble.POSITIVE_INFINITY
. *
* The main task of this class is to calculate the risk-neutral drift and the * corresponding numeraire given the covariance model. * * The calibration of the covariance structure is not part of this class. For the calibration * of parametric models of the instantaneous covariance see * {@link net.finmath.montecarlo.interestrate.modelplugins.AbstractLIBORCovarianceModelParametric#getCloneCalibrated(LIBORMarketModelInterface, AbstractLIBORMonteCarloProduct[], double[], double[], Map)}. * * @author Christian Fries * @version 1.2 * @see net.finmath.montecarlo.process.AbstractProcessInterface The interface for numerical schemes. * @see net.finmath.montecarlo.model.AbstractModelInterface The interface for models provinding parameters to numerical schemes. * @see net.finmath.montecarlo.interestrate.modelplugins.AbstractLIBORCovarianceModel The abstract covariance model plug ins. * @see net.finmath.montecarlo.interestrate.modelplugins.AbstractLIBORCovarianceModelParametric A parametic covariance model including a generic calibration algorithm. */ public class LIBORMarketModel extends AbstractModel implements LIBORMarketModelInterface { public enum Driftapproximation { EULER, LINE_INTEGRAL, PREDICTOR_CORRECTOR } public enum Measure { SPOT, TERMINAL } public enum StateSpace { NORMAL, LOGNORMAL } private final TimeDiscretizationInterface liborPeriodDiscretization; private String forwardCurveName; private AnalyticModelInterface curveModel; private ForwardCurveInterface forwardRateCurve; private DiscountCurveInterface discountCurve; private final AbstractRandomVariableFactory randomVariableFactory; private AbstractLIBORCovarianceModel covarianceModel; private AbstractSwaptionMarketData swaptionMarketData; private Driftapproximation driftApproximationMethod = Driftapproximation.EULER; private Measure measure = Measure.SPOT; private StateSpace stateSpace = StateSpace.LOGNORMAL; private double liborCap = 1E5; // This is a cache of the integrated covariance. private double[][][] integratedLIBORCovariance; private final Object integratedLIBORCovarianceLazyInitLock = new Object(); // Cache for the numeraires, needs to be invalidated if process changes private final ConcurrentHashMapnumeraires; private AbstractProcessInterface numerairesProcess = null; public static class CalibrationItem { public final AbstractLIBORMonteCarloProduct calibrationProduct; public final double calibrationTargetValue; public final double calibrationWeight; public CalibrationItem(AbstractLIBORMonteCarloProduct calibrationProduct, double calibrationTargetValue, double calibrationWeight) { super(); this.calibrationProduct = calibrationProduct; this.calibrationTargetValue = calibrationTargetValue; this.calibrationWeight = calibrationWeight; } @Override public String toString() { return "CalibrationItem [calibrationProduct=" + calibrationProduct + ", calibrationTargetValue=" + calibrationTargetValue + ", calibrationWeight=" + calibrationWeight + "]"; } } /** * Creates a LIBOR Market Model for given covariance. *
* If calibrationItems in non-empty and the covariance model is a parametric model, * the covariance will be replaced by a calibrate version of the same model, i.e., * the LIBOR Market Model will be calibrated. *
* The mapproperties
allows to configure the model. The following keys may be used: **
* * @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure). * @param analyticModel The associated analytic model of this model (containing the associated market data objects like curve). * @param forwardRateCurve The initial values for the forward rates. * @param discountCurve The discount curve to use. This will create an LMM model with a deterministic zero-spread discounting adjustment. * @param randomVariableFactory The random variable factory used to create the inital values of the model. * @param covarianceModel The covariance model to use. * @param calibrationItems The vector of calibration items (a union of a product, target value and weight) for the objective function sum weight(i) * (modelValue(i)-targetValue(i). * @param properties Key value map specifying properties like- *
*measure
: Possible values: **
*- *
*SPOT
(String
): Simulate under spot measure. *- *
*TERMINAL
(String
): Simulate under terminal measure. *- *
*stateSpace
: Possible values: **
*- *
*LOGNORMAL
(String
): Simulate L = exp(Y). *- *
*NORMAL
(String
): Simulate L = Y. *- *
*liborCap
: An optionalDouble
value applied as a cap to the LIBOR rates. * May be used to limit the simulated valued to prevent values attaining POSITIVE_INFINITY and * numerical problems. To disable the cap, setliborCap
toDouble.POSITIVE_INFINITY
. *- *
*calibrationParameters
: Possible values: **
*- *
*Map<String,Object>
a parameter map with the following key/value pairs: **
*- *
*accuracy
:Double
specifying the required solver accuracy. *- *
*maxIterations
:Integer
specifying the maximum iterations for the solver. *measure
andstateSpace
. * @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via thecause()
method. */ public LIBORMarketModel( TimeDiscretizationInterface liborPeriodDiscretization, AnalyticModelInterface analyticModel, ForwardCurveInterface forwardRateCurve, DiscountCurveInterface discountCurve, AbstractRandomVariableFactory randomVariableFactory, AbstractLIBORCovarianceModel covarianceModel, CalibrationItem[] calibrationItems, Mapproperties ) throws CalculationException { // Set some properties if(properties != null && properties.containsKey("measure")) measure = Measure.valueOf(((String)properties.get("measure")).toUpperCase()); if(properties != null && properties.containsKey("stateSpace")) stateSpace = StateSpace.valueOf(((String)properties.get("stateSpace")).toUpperCase()); if(properties != null && properties.containsKey("liborCap")) liborCap = (Double)properties.get("liborCap"); Map calibrationParameters = null; if(properties != null && properties.containsKey("calibrationParameters")) calibrationParameters = (Map )properties.get("calibrationParameters"); this.liborPeriodDiscretization = liborPeriodDiscretization; this.curveModel = analyticModel; this.forwardRateCurve = forwardRateCurve; this.discountCurve = discountCurve; this.randomVariableFactory = randomVariableFactory; this.covarianceModel = covarianceModel; double[] times = new double[liborPeriodDiscretization.getNumberOfTimeSteps()]; for(int i=0; i 0) { AbstractLIBORCovarianceModelParametric covarianceModelParametric = null; try { covarianceModelParametric = (AbstractLIBORCovarianceModelParametric)covarianceModel; } catch(Exception e) { throw new ClassCastException("Calibration is currently restricted to parametric covariance models (AbstractLIBORCovarianceModelParametric)."); } // @TODO Should be more elegant. Convert array for constructor AbstractLIBORMonteCarloProduct[] calibrationProducts = new AbstractLIBORMonteCarloProduct[calibrationItems.length]; double[] calibrationTargetValues = new double[calibrationItems.length]; double[] calibrationWeights = new double[calibrationItems.length]; for(int i=0; i (); } /** * Creates a LIBOR Market Model for given covariance. *
* If calibrationItems in non-empty and the covariance model is a parametric model, * the covariance will be replaced by a calibrate version of the same model, i.e., * the LIBOR Market Model will be calibrated. *
* The mapproperties
allows to configure the model. The following keys may be used: **
* * @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure). * @param analyticModel The associated analytic model of this model (containing the associated market data objects like curve). * @param forwardRateCurve The initial values for the forward rates. * @param discountCurve The discount curve to use. This will create an LMM model with a deterministic zero-spread discounting adjustment. * @param covarianceModel The covariance model to use. * @param calibrationItems The vector of calibration items (a union of a product, target value and weight) for the objective function sum weight(i) * (modelValue(i)-targetValue(i). * @param properties Key value map specifying properties like- *
*measure
: Possible values: **
*- *
*SPOT
(String
): Simulate under spot measure. *- *
*TERMINAL
(String
): Simulate under terminal measure. *- *
*stateSpace
: Possible values: **
*- *
*LOGNORMAL
(String
): Simulate L = exp(Y). *- *
*NORMAL
(String
): Simulate L = Y. *- *
*liborCap
: An optionalDouble
value applied as a cap to the LIBOR rates. * May be used to limit the simulated valued to prevent values attaining POSITIVE_INFINITY and * numerical problems. To disable the cap, setliborCap
toDouble.POSITIVE_INFINITY
. *- *
*calibrationParameters
: Possible values: **
*- *
*Map<String,Object>
a parameter map with the following key/value pairs: **
*- *
*accuracy
:Double
specifying the required solver accuracy. *- *
*maxIterations
:Integer
specifying the maximum iterations for the solver. *measure
andstateSpace
. * @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via thecause()
method. */ public LIBORMarketModel( TimeDiscretizationInterface liborPeriodDiscretization, AnalyticModelInterface analyticModel, ForwardCurveInterface forwardRateCurve, DiscountCurveInterface discountCurve, AbstractLIBORCovarianceModel covarianceModel, CalibrationItem[] calibrationItems, Mapproperties ) throws CalculationException { this(liborPeriodDiscretization, analyticModel, forwardRateCurve, discountCurve, new RandomVariableFactory(), covarianceModel, calibrationItems, properties); } /** * Creates a LIBOR Market Model for given covariance. * * @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure). * @param forwardRateCurve The initial values for the forward rates. * @param covarianceModel The covariance model to use. * @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via the cause()
method. */ public LIBORMarketModel( TimeDiscretizationInterface liborPeriodDiscretization, ForwardCurveInterface forwardRateCurve, AbstractLIBORCovarianceModel covarianceModel ) throws CalculationException { this(liborPeriodDiscretization, forwardRateCurve, new DiscountCurveFromForwardCurve(forwardRateCurve), covarianceModel, new CalibrationItem[0], null); } /** * Creates a LIBOR Market Model for given covariance. * * @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure). * @param forwardRateCurve The initial values for the forward rates. * @param discountCurve The discount curve to use. This will create an LMM model with a deterministic zero-spread discounting adjustment. * @param covarianceModel The covariance model to use. * @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via thecause()
method. */ public LIBORMarketModel( TimeDiscretizationInterface liborPeriodDiscretization, ForwardCurveInterface forwardRateCurve, DiscountCurveInterface discountCurve, AbstractLIBORCovarianceModel covarianceModel ) throws CalculationException { this(liborPeriodDiscretization, forwardRateCurve, discountCurve, covarianceModel, new CalibrationItem[0], null); } /** * Creates a LIBOR Market Model using a given covariance model and calibrating this model * to given swaption volatility data. * * @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure). * @param forwardRateCurve The initial values for the forward rates. * @param covarianceModel The covariance model to use. * @param swaptionMarketData The set of swaption values to calibrate to. * @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via thecause()
method. */ public LIBORMarketModel( TimeDiscretizationInterface liborPeriodDiscretization, ForwardCurveInterface forwardRateCurve, AbstractLIBORCovarianceModel covarianceModel, AbstractSwaptionMarketData swaptionMarketData ) throws CalculationException { this(liborPeriodDiscretization, forwardRateCurve, new DiscountCurveFromForwardCurve(forwardRateCurve), covarianceModel, swaptionMarketData, null); } /** * Creates a LIBOR Market Model for given covariance. * * @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure). * @param forwardRateCurve The initial values for the forward rates. * @param discountCurve The discount curve to use. This will create an LMM model with a deterministic zero-spread discounting adjustment. * @param covarianceModel The covariance model to use. * @param swaptionMarketData The set of swaption values to calibrate to. * @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via thecause()
method. */ public LIBORMarketModel( TimeDiscretizationInterface liborPeriodDiscretization, ForwardCurveInterface forwardRateCurve, DiscountCurveInterface discountCurve, AbstractLIBORCovarianceModel covarianceModel, AbstractSwaptionMarketData swaptionMarketData ) throws CalculationException { this( liborPeriodDiscretization, forwardRateCurve, discountCurve, covarianceModel, swaptionMarketData, null ); } /** * Creates a LIBOR Market Model for given covariance. * * @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure). * @param forwardRateCurve The initial values for the forward rates. * @param discountCurve The discount curve to use. This will create an LMM model with a deterministic zero-spread discounting adjustment. * @param covarianceModel The covariance model to use. * @param swaptionMarketData The set of swaption values to calibrate to. * @param properties Key value map specifying properties likemeasure
andstateSpace
. * @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via thecause()
method. */ public LIBORMarketModel( TimeDiscretizationInterface liborPeriodDiscretization, ForwardCurveInterface forwardRateCurve, DiscountCurveInterface discountCurve, AbstractLIBORCovarianceModel covarianceModel, AbstractSwaptionMarketData swaptionMarketData, Mapproperties ) throws CalculationException { this( liborPeriodDiscretization, forwardRateCurve, discountCurve, covarianceModel, getCalibrationItems( liborPeriodDiscretization, forwardRateCurve, swaptionMarketData, // Condition under which we use analytic approximation (properties == null || properties.get("stateSpace") == null || ((String)properties.get("stateSpace")).toUpperCase().equals(StateSpace.LOGNORMAL.name())) && AbstractLIBORCovarianceModelParametric.class.isAssignableFrom(covarianceModel.getClass()) ), properties ); } /** * Creates a LIBOR Market Model for given covariance. *
* If calibrationItems in non-empty and the covariance model is a parametric model, * the covariance will be replaced by a calibrate version of the same model, i.e., * the LIBOR Market Model will be calibrated. *
* The mapproperties
allows to configure the model. The following keys may be used: **
* * @param liborPeriodDiscretization The discretization of the interest rate curve into forward rates (tenor structure). * @param forwardRateCurve The initial values for the forward rates. * @param discountCurve The discount curve to use. This will create an LMM model with a deterministic zero-spread discounting adjustment. * @param covarianceModel The covariance model to use. * @param calibrationItems The vector of calibration items (a union of a product, target value and weight) for the objective function sum weight(i) * (modelValue(i)-targetValue(i). * @param properties Key value map specifying properties like- *
*measure
: Possible values: **
*- *
*SPOT
(String
): Simulate under spot measure. *- *
*TERMINAL
(String
): Simulate under terminal measure. *- *
*stateSpace
: Possible values: **
*- *
*LOGNORMAL
(String
): Simulate L = exp(Y). *- *
*NORMAL
(String
): Simulate L = Y. *- *
*calibrationParameters
: Possible values: **
*- *
*Map<String,Object>
a parameter map with the following key/value pairs: **
*- *
*accuracy
:Double
specifying the required solver accuracy. *- *
*maxIterations
:Integer
specifying the maximum iterations for the solver. *measure
andstateSpace
. * @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via thecause()
method. */ public LIBORMarketModel( TimeDiscretizationInterface liborPeriodDiscretization, ForwardCurveInterface forwardRateCurve, DiscountCurveInterface discountCurve, AbstractLIBORCovarianceModel covarianceModel, CalibrationItem[] calibrationItems, Mapproperties ) throws CalculationException { this(liborPeriodDiscretization, null, forwardRateCurve, discountCurve, covarianceModel, calibrationItems, properties); } private static CalibrationItem[] getCalibrationItems(TimeDiscretizationInterface liborPeriodDiscretization, ForwardCurveInterface forwardCurve, AbstractSwaptionMarketData swaptionMarketData, boolean isUseAnalyticApproximation) { if(swaptionMarketData == null) return null; TimeDiscretizationInterface optionMaturities = swaptionMarketData.getOptionMaturities(); TimeDiscretizationInterface tenor = swaptionMarketData.getTenor(); double swapPeriodLength = swaptionMarketData.getSwapPeriodLength(); ArrayList calibrationItems = new ArrayList (); for(int exerciseIndex=0; exerciseIndex<=optionMaturities.getNumberOfTimeSteps(); exerciseIndex++) { for(int tenorIndex=0; tenorIndex<=tenor.getNumberOfTimeSteps()-exerciseIndex; tenorIndex++) { // Create a swaption double exerciseDate = optionMaturities.getTime(exerciseIndex); double swapLength = tenor.getTime(tenorIndex); if(liborPeriodDiscretization.getTimeIndex(exerciseDate) < 0) continue; if(liborPeriodDiscretization.getTimeIndex(exerciseDate+swapLength) <= liborPeriodDiscretization.getTimeIndex(exerciseDate)) continue; int numberOfPeriods = (int)(swapLength / swapPeriodLength); double[] fixingDates = new double[numberOfPeriods]; double[] paymentDates = new double[numberOfPeriods]; double[] swapTenorTimes = new double[numberOfPeriods+1]; for(int periodStartIndex=0; periodStartIndex t for which the numeraire should be returned N(t). * @return The numeraire at the specified time as RandomVariableInterface
* @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via thecause()
method. */ @Override public RandomVariableInterface getNumeraire(double time) throws CalculationException { int timeIndex = getLiborPeriodIndex(time); if(timeIndex < 0) { // Interpolation of Numeraire: log linear interpolation. int upperIndex = -timeIndex-1; int lowerIndex = upperIndex-1; if(lowerIndex < 0) throw new IllegalArgumentException("Numeraire requested for time " + time + ". Unsupported"); double alpha = (time-getLiborPeriod(lowerIndex)) / (getLiborPeriod(upperIndex) - getLiborPeriod(lowerIndex)); RandomVariableInterface numeraire = getNumeraire(getLiborPeriod(upperIndex)).log().mult(alpha).add(getNumeraire(getLiborPeriod(lowerIndex)).log().mult(1.0-alpha)).exp(); /* * Adjust for discounting, i.e. funding or collateralization */ if(discountCurve != null) { // This includes a control for zero bonds double deterministicNumeraireAdjustment = numeraire.invert().getAverage() / discountCurve.getDiscountFactor(curveModel, time); numeraire = numeraire.mult(deterministicNumeraireAdjustment); } return numeraire; } /* * Calculate the numeraire, when time is part of liborPeriodDiscretization */ /* * Check if numeraire cache is valid (i.e. process did not change) */ if(getProcess() != numerairesProcess) { numeraires.clear(); numerairesProcess = getProcess(); } /* * Check if numeraire is part of the cache */ RandomVariableInterface numeraire = numeraires.get(timeIndex); if(numeraire == null) { /* * Calculate the numeraire for timeIndex */ // Initialize to 1.0 numeraire = getRandomVariableForConstant(1.0); // Get the start and end of the product int firstLiborIndex, lastLiborIndex; if(measure == Measure.TERMINAL) { firstLiborIndex = getLiborPeriodIndex(time); if(firstLiborIndex < 0) { throw new CalculationException("Simulation time discretization not part of forward rate tenor discretization."); } lastLiborIndex = liborPeriodDiscretization.getNumberOfTimeSteps()-1; } else if(measure == Measure.SPOT) { // Spot measure firstLiborIndex = 0; lastLiborIndex = getLiborPeriodIndex(time)-1; } else { throw new CalculationException("Numeraire not implemented for specified measure."); } // The product for(int liborIndex = firstLiborIndex; liborIndex<=lastLiborIndex; liborIndex++) { RandomVariableInterface libor = getLIBOR(getTimeIndex(Math.min(time,liborPeriodDiscretization.getTime(liborIndex))), liborIndex); double periodLength = liborPeriodDiscretization.getTimeStep(liborIndex); if(measure == Measure.SPOT) { numeraire = numeraire.accrue(libor, periodLength); } else { numeraire = numeraire.discount(libor, periodLength); } } numeraires.put(timeIndex, numeraire); } /* * Adjust for discounting, i.e. funding or collateralization */ if(discountCurve != null) { // This includes a control for zero bonds double deterministicNumeraireAdjustment = numeraire.invert().getAverage() / discountCurve.getDiscountFactor(curveModel, time); numeraire = numeraire.mult(deterministicNumeraireAdjustment); } return numeraire; } @Override public RandomVariableInterface[] getInitialState() { double[] liborInitialStates = new double[liborPeriodDiscretization.getNumberOfTimeSteps()]; for(int timeIndex=0; timeIndexMeasure.SPOT Measure.TERMINAL
- depending how the * model object was constructed. ForMeasure.TERMINAL
the j-th entry of the return value is the random variable * \[ * \mu_{j}^{\mathbb{Q}^{P(T_{n})}}(t) \ = \ - \mathop{\sum_{l\geq j+1}}_{l\leq n-1} \frac{\delta_{l}}{1+\delta_{l} L_{l}(t)} (\lambda_{j}(t) \cdot \lambda_{l}(t)) * \] * and forMeasure.SPOT
the j-th entry of the return value is the random variable * \[ * \mu_{j}^{\mathbb{Q}^{N}}(t) \ = \ \sum_{m(t) < l\leq j} \frac{\delta_{l}}{1+\delta_{l} L_{l}(t)} (\lambda_{j}(t) \cdot \lambda_{l}(t)) * \] * where \( \lambda_{j} \) is the vector for factor loadings for the j-th component of the stochastic process (that is, the diffusion part is * \( \sum_{k=1}^m \lambda_{j,k} \mathrm{d}W_{k} \)). * * Note: The scalar product of the factor loadings determines the instantaneous covariance. If the model is written in log-coordinates (using exp as a state space transform), we find * \(\lambda_{j} \cdot \lambda_{l} = \sum_{k=1}^m \lambda_{j,k} \lambda_{l,k} = \sigma_{j} \sigma_{l} \rho_{j,l} \). * If the model is written without a state space transformation (in its orignial coordinates) then \(\lambda_{j} \cdot \lambda_{l} = \sum_{k=1}^m \lambda_{j,k} \lambda_{l,k} = L_{j} L_{l} \sigma_{j} \sigma_{l} \rho_{j,l} \). * * * @see net.finmath.montecarlo.interestrate.LIBORMarketModel#getNumeraire(double) The calculation of the drift is consistent with the calculation of the numeraire ingetNumeraire
. * @see net.finmath.montecarlo.interestrate.LIBORMarketModel#getFactorLoading(int, int, RandomVariableInterface[]) The factor loading \( \lambda_{j,k} \). * * @param timeIndex Time index i for which the drift should be returned μ(ti). * @param realizationAtTimeIndex Time current forward rate vector at time index i which should be used in the calculation. * @return The drift vector μ(ti) asRandomVariable[]
*/ @Override public RandomVariableInterface[] getDrift(int timeIndex, RandomVariableInterface[] realizationAtTimeIndex, RandomVariableInterface[] realizationPredictor) { double time = getTime(timeIndex); int firstLiborIndex = this.getLiborPeriodIndex(time)+1; if(firstLiborIndex<0) firstLiborIndex = -firstLiborIndex-1 + 1; RandomVariableInterface zero = getRandomVariableForConstant(0.0); // Allocate drift vector and initialize to zero (will be used to sum up drift components) RandomVariableInterface[] drift = new RandomVariableInterface[getNumberOfComponents()]; for(int componentIndex=firstLiborIndex; componentIndex=firstLiborIndex; componentIndex--) { double periodLength = liborPeriodDiscretization.getTimeStep(componentIndex); RandomVariableInterface libor = realizationAtTimeIndex[componentIndex]; RandomVariableInterface oneStepMeasureTransform = getRandomVariableForConstant(periodLength).discount(libor, periodLength); if(stateSpace == StateSpace.LOGNORMAL) oneStepMeasureTransform = oneStepMeasureTransform.mult(libor); RandomVariableInterface[] factorLoading = getFactorLoading(timeIndex, componentIndex, realizationAtTimeIndex); for(int factorIndex=0; factorIndex = liborPeriodDiscretization.getNumberOfTimes() || timeIndex < 0) { throw new ArrayIndexOutOfBoundsException("Index for LIBOR period discretization out of bounds: " + timeIndex + "."); } return liborPeriodDiscretization.getTime(timeIndex); } /* (non-Javadoc) * @see net.finmath.montecarlo.interestrate.LIBORMarketModelInterface#getLiborPeriodIndex(double) */ @Override public int getLiborPeriodIndex(double time) { return liborPeriodDiscretization.getTimeIndex(time); } /* (non-Javadoc) * @see net.finmath.montecarlo.interestrate.LIBORMarketModelInterface#getLiborPeriodDiscretization() */ @Override public TimeDiscretizationInterface getLiborPeriodDiscretization() { return liborPeriodDiscretization; } /** * @return Returns the measure. */ public Measure getMeasure() { return measure; } /* (non-Javadoc) * @see net.finmath.montecarlo.interestrate.LIBORMarketModelInterface#getIntegratedLIBORCovariance() */ @Override public double[][][] getIntegratedLIBORCovariance() { synchronized (integratedLIBORCovarianceLazyInitLock) { if(integratedLIBORCovariance == null) { TimeDiscretizationInterface liborPeriodDiscretization = getLiborPeriodDiscretization(); TimeDiscretizationInterface simulationTimeDiscretization = getCovarianceModel().getTimeDiscretization(); integratedLIBORCovariance = new double[simulationTimeDiscretization.getNumberOfTimeSteps()][liborPeriodDiscretization.getNumberOfTimeSteps()][liborPeriodDiscretization.getNumberOfTimeSteps()]; for(int timeIndex = 0; timeIndex < simulationTimeDiscretization.getNumberOfTimeSteps(); timeIndex++) { double dt = simulationTimeDiscretization.getTime(timeIndex+1) - simulationTimeDiscretization.getTime(timeIndex); RandomVariableInterface[][] factorLoadings = new RandomVariableInterface[liborPeriodDiscretization.getNumberOfTimeSteps()][]; // Prefetch factor loadings for(int componentIndex = 0; componentIndex < liborPeriodDiscretization.getNumberOfTimeSteps(); componentIndex++) { factorLoadings[componentIndex] = getCovarianceModel().getFactorLoading(timeIndex, componentIndex, null); } for(int componentIndex1 = 0; componentIndex1 < liborPeriodDiscretization.getNumberOfTimeSteps(); componentIndex1++) { RandomVariableInterface[] factorLoadingOfComponent1 = factorLoadings[componentIndex1]; // Sum the libor cross terms (use symmetry) for(int componentIndex2 = componentIndex1; componentIndex2 < liborPeriodDiscretization.getNumberOfTimeSteps(); componentIndex2++) { double integratedLIBORCovarianceValue = 0.0; if(getLiborPeriod(componentIndex1) > getTime(timeIndex)) { RandomVariableInterface[] factorLoadingOfComponent2 = factorLoadings[componentIndex2]; for(int factorIndex = 0; factorIndex < getNumberOfFactors(); factorIndex++) { integratedLIBORCovarianceValue += factorLoadingOfComponent1[factorIndex].get(0) * factorLoadingOfComponent2[factorIndex].get(0) * dt; } } integratedLIBORCovariance[timeIndex][componentIndex1][componentIndex2] = integratedLIBORCovarianceValue; } } } // Integrate over time (i.e. sum up). for(int timeIndex = 1; timeIndex < simulationTimeDiscretization.getNumberOfTimeSteps(); timeIndex++) { double[][] prevIntegratedLIBORCovariance = integratedLIBORCovariance[timeIndex-1]; double[][] thisIntegratedLIBORCovariance = integratedLIBORCovariance[timeIndex]; for(int componentIndex1 = 0; componentIndex1 < liborPeriodDiscretization.getNumberOfTimeSteps(); componentIndex1++) { for(int componentIndex2 = componentIndex1; componentIndex2 < liborPeriodDiscretization.getNumberOfTimeSteps(); componentIndex2++) { thisIntegratedLIBORCovariance[componentIndex1][componentIndex2] = prevIntegratedLIBORCovariance[componentIndex1][componentIndex2] + thisIntegratedLIBORCovariance[componentIndex1][componentIndex2]; thisIntegratedLIBORCovariance[componentIndex2][componentIndex1] = thisIntegratedLIBORCovariance[componentIndex1][componentIndex2]; } } } } } return integratedLIBORCovariance; } @Override public Object clone() { try { Map properties = new HashMap (); properties.put("measure", measure.name()); properties.put("stateSpace", stateSpace.name()); return new LIBORMarketModel(getLiborPeriodDiscretization(), getAnalyticModel(), getForwardRateCurve(), getDiscountCurve(), randomVariableFactory, covarianceModel, new CalibrationItem[0], properties); } catch (CalculationException e) { return null; } } @Override public AnalyticModelInterface getAnalyticModel() { return curveModel; } @Override public DiscountCurveInterface getDiscountCurve() { return discountCurve; } @Override public ForwardCurveInterface getForwardRateCurve() { return forwardRateCurve; } /** * Return the swaption market data used for calibration (if any, may be null). * * @return The swaption market data used for calibration (if any, may be null). */ public AbstractSwaptionMarketData getSwaptionMarketData() { return swaptionMarketData; } /* (non-Javadoc) * @see net.finmath.montecarlo.interestrate.LIBORMarketModelInterface#getCovarianceModel() */ @Override public AbstractLIBORCovarianceModel getCovarianceModel() { return covarianceModel; } /** * @param covarianceModel A covariance model * @return A new LIBORMarketModel
using the specified covariance model. */ @Override public LIBORMarketModel getCloneWithModifiedCovarianceModel(AbstractLIBORCovarianceModel covarianceModel) { LIBORMarketModel model = (LIBORMarketModel)this.clone(); model.covarianceModel = covarianceModel; return model; } @Override public LIBORMarketModel getCloneWithModifiedData(MapdataModified) throws CalculationException { TimeDiscretizationInterface liborPeriodDiscretization = this.liborPeriodDiscretization; AnalyticModelInterface analyticModel = this.curveModel; ForwardCurveInterface forwardRateCurve = this.forwardRateCurve; DiscountCurveInterface discountCurve = this.discountCurve; AbstractLIBORCovarianceModel covarianceModel = this.covarianceModel; AbstractSwaptionMarketData swaptionMarketData = null; // No recalibration, unless new swaption data is specified Map properties = new HashMap (); properties.put("measure", measure.name()); properties.put("stateSpace", stateSpace.name()); if(dataModified.containsKey("liborPeriodDiscretization")) { liborPeriodDiscretization = (TimeDiscretizationInterface)dataModified.get("liborPeriodDiscretization"); } if(dataModified.containsKey("forwardRateCurve")) { forwardRateCurve = (ForwardCurveInterface)dataModified.get("forwardRateCurve"); } if(dataModified.containsKey("discountCurve")) { discountCurve = (DiscountCurveInterface)dataModified.get("discountCurve"); } if(dataModified.containsKey("forwardRateShift")) { throw new RuntimeException("Forward rate shift clone currently disabled."); } if(dataModified.containsKey("covarianceModel")) { covarianceModel = (AbstractLIBORCovarianceModel)dataModified.get("covarianceModel"); } if(dataModified.containsKey("swaptionMarketData")) { swaptionMarketData = (AbstractSwaptionMarketData)dataModified.get("swaptionMarketData"); } LIBORMarketModel newModel = new LIBORMarketModel(liborPeriodDiscretization, forwardRateCurve, discountCurve, covarianceModel, swaptionMarketData, properties); newModel.curveModel = analyticModel; return newModel; } @Override public String toString() { return "LIBORMarketModel [liborPeriodDiscretization=" + liborPeriodDiscretization + ", forwardCurveName=" + forwardCurveName + ", curveModel=" + curveModel + ", forwardRateCurve=" + forwardRateCurve + ", discountCurve=" + discountCurve + ", covarianceModel=" + covarianceModel + ", driftApproximationMethod=" + driftApproximationMethod + ", measure=" + measure + ", stateSpace=" + stateSpace + "]"; } }
© 2015 - 2025 Weber Informatics LLC | Privacy Policy