diff --git a/src/QLNet.Old/QLNet.Old.csproj b/src/QLNet.Old/QLNet.Old.csproj index ce6042536..e820a2466 100644 --- a/src/QLNet.Old/QLNet.Old.csproj +++ b/src/QLNet.Old/QLNet.Old.csproj @@ -1005,6 +1005,9 @@ Methods\Finitedifferences\Meshers\Fdm1dMesher.cs + + Methods\Finitedifferences\Meshers\FdmSimpleProcess1dMesher.cs + Methods\Finitedifferences\Meshers\FdmBlackScholesMesher.cs @@ -1023,6 +1026,9 @@ Methods\Finitedifferences\Operators\FdmBlackScholesOp.cs + + Methods\Finitedifferences\Operators\FdmHullWhiteOp.cs + Methods\Finitedifferences\Operators\FdmLinearOp.cs @@ -1080,6 +1086,9 @@ Methods\Finitedifferences\Solvers\FdmBlackScholesSolver.cs + + Methods\Finitedifferences\Solvers\FdmHullWhiteSolver.cs + Methods\Finitedifferences\Solvers\FdmSolverDesc.cs @@ -1110,6 +1119,12 @@ Methods\Finitedifferences\Utilities\FdmInnerValueCalculator.cs + + Methods\Finitedifferences\Utilities\FdmAffineModelSwapInnerValue.cs + + + Methods\Finitedifferences\Utilities\FdmAffineModelTermStructure.cs + Methods\Finitedifferences\Utilities\FdmMesherIntegral.cs @@ -1487,6 +1502,9 @@ Pricingengines\vanilla\FdBlackScholesVanillaEngine.cs + + Pricingengines\vanilla\FdHullWhiteSwaptionEngine.cs + Pricingengines\vanilla\FDAmericanEngine.cs diff --git a/src/QLNet/Methods/Finitedifferences/Meshers/FdmSimpleProcess1dMesher.cs b/src/QLNet/Methods/Finitedifferences/Meshers/FdmSimpleProcess1dMesher.cs new file mode 100644 index 000000000..3774802ef --- /dev/null +++ b/src/QLNet/Methods/Finitedifferences/Meshers/FdmSimpleProcess1dMesher.cs @@ -0,0 +1,74 @@ +/* + Copyright (C) 2017 Jean-Camille Tournier (jean-camille.tournier@avivainvestors.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available online at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the license for more details. +*/ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace QLNet +{ + /// + /// One-dimensional grid mesher + /// + public class FdmSimpleProcess1DMesher : Fdm1dMesher + { + public FdmSimpleProcess1DMesher(int size, + StochasticProcess1D process, + double maturity, int tAvgSteps = 10, + double epsilon = 0.0001, + double? mandatoryPoint = null) + : base(size) + { + locations_ = new InitializedList(locations_.Count, 0.0); + for (int l=1; l<=tAvgSteps; ++l) + { + double t = (maturity*l)/tAvgSteps; + + double mp = (mandatoryPoint != null) ? mandatoryPoint.Value + : process.x0(); + + double qMin = Math.Min(Math.Min(mp, process.x0()), + process.evolve(0, process.x0(), t, + new InverseCumulativeNormal().value(epsilon))); + double qMax = Math.Max(Math.Max(mp, process.x0()), + process.evolve(0, process.x0(), t, + new InverseCumulativeNormal().value(1 - epsilon))); + + double dp = (1 - 2 * epsilon) / (size - 1); + double p = epsilon; + locations_[0] += qMin; + + for (int i=1; i < size-1; ++i) + { + p += dp; + locations_[i] += process.evolve(0, process.x0(), t, + new InverseCumulativeNormal().value(p)); + } + locations_[locations_.Count - 1] += qMax; + } + locations_ = locations_.Select(x => x / tAvgSteps).ToList(); + for (int i=0; i < size-1; ++i) + { + dminus_[i+1] = dplus_[i] = locations_[i+1] - locations_[i]; + } + + dplus_[dplus_.Count - 1] = null; + dminus_[0] = null; + } + } +} diff --git a/src/QLNet/Methods/Finitedifferences/Operators/FdmHullWhiteOp.cs b/src/QLNet/Methods/Finitedifferences/Operators/FdmHullWhiteOp.cs new file mode 100644 index 000000000..1f4edc727 --- /dev/null +++ b/src/QLNet/Methods/Finitedifferences/Operators/FdmHullWhiteOp.cs @@ -0,0 +1,105 @@ +/* + Copyright (C) 2017 Jean-Camille Tournier (jean-camille.tournier@avivainvestors.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available online at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the license for more details. +*/ +using System; +using System.Collections.Generic; + +namespace QLNet +{ + public class FdmHullWhiteOp : FdmLinearOpComposite + { + public FdmHullWhiteOp(FdmMesher mesher, + HullWhite model, + int direction) + { + x_ = mesher.locations(direction); + dzMap_ = new TripleBandLinearOp(new FirstDerivativeOp(direction, mesher).mult(-1.0 * x_ * model.a()).add( + new SecondDerivativeOp(direction, mesher).mult(0.5 * model.sigma() * model.sigma() + * new Vector(mesher.layout().size(), 1.0)))); + mapT_ = new TripleBandLinearOp(direction, mesher); + direction_ = direction; + model_ = model; + } + public override int size() { return 1; } + + //! Time \f$t1 <= t2\f$ is required + public override void setTime(double t1, double t2) + { + OneFactorModel.ShortRateDynamics dynamics = model_.dynamics(); + + double phi = 0.5* ( dynamics.shortRate(t1, 0.0) + + dynamics.shortRate(t2, 0.0)); + + mapT_.axpyb(new Vector(), dzMap_, dzMap_, -1.0 * (x_ + phi)); + } + + public override Vector apply(Vector r) + { + return mapT_.apply(r); + } + + public override Vector apply_mixed(Vector r) { + Vector retVal = new Vector(r.size(), 0.0); + return retVal; + } + + public override Vector apply_direction(int direction, Vector r) { + if (direction == direction_) + return mapT_.apply(r); + else { + Vector retVal = new Vector(r.size(), 0.0); + return retVal; + } + } + public override Vector solve_splitting(int direction, Vector r, double s) { + if (direction == direction_) + return mapT_.solve_splitting(r, s, 1.0); + else { + Vector retVal = new Vector(r.size(), 0.0); + return retVal; + } + } + public override Vector preconditioner(Vector r, double s) { return solve_splitting(direction_, r, s); } + + public override List toMatrixDecomp() + { + List retVal = new InitializedList(1, mapT_.toMatrix()); + return retVal; + } + + #region IOperator interface + public override IOperator identity(int size) { return null; } + public override Vector applyTo(Vector v) { return new Vector(); } + public override Vector solveFor(Vector rhs) { return new Vector(); } + + public override IOperator multiply(double a, IOperator D) { return null; } + public override IOperator add(IOperator A, IOperator B) { return null; } + public override IOperator subtract(IOperator A, IOperator B) { return null; } + + public override bool isTimeDependent() { return false; } + public override void setTime(double t) { } + public override object Clone() { return this.MemberwiseClone(); } + #endregion + + protected HullWhite model_; + protected Vector x_; + protected TripleBandLinearOp dzMap_; + protected TripleBandLinearOp mapT_; + protected int direction_; + } +} diff --git a/src/QLNet/Methods/Finitedifferences/Operators/TripleBandLinearOp.cs b/src/QLNet/Methods/Finitedifferences/Operators/TripleBandLinearOp.cs index 707b286ea..7f55a9196 100644 --- a/src/QLNet/Methods/Finitedifferences/Operators/TripleBandLinearOp.cs +++ b/src/QLNet/Methods/Finitedifferences/Operators/TripleBandLinearOp.cs @@ -65,12 +65,12 @@ public TripleBandLinearOp(int direction, FdmMesher mesher) public TripleBandLinearOp(TripleBandLinearOp m) { direction_ = m.direction_; - i0_ = new List(m.mesher_.layout().size()); - i2_ = new List(m.mesher_.layout().size()); - reverseIndex_ = new List(m.mesher_.layout().size()); - lower_ = new List(m.mesher_.layout().size()); - diag_ = new List(m.mesher_.layout().size()); - upper_ = new List(m.mesher_.layout().size()); + i0_ = new InitializedList(m.mesher_.layout().size()); + i2_ = new InitializedList(m.mesher_.layout().size()); + reverseIndex_ = new InitializedList(m.mesher_.layout().size()); + lower_ = new InitializedList(m.mesher_.layout().size()); + diag_ = new InitializedList(m.mesher_.layout().size()); + upper_ = new InitializedList(m.mesher_.layout().size()); mesher_ = m.mesher_; int len = m.mesher_.layout().size(); diff --git a/src/QLNet/Methods/Finitedifferences/Solvers/FdmHullWhiteSolver.cs b/src/QLNet/Methods/Finitedifferences/Solvers/FdmHullWhiteSolver.cs new file mode 100644 index 000000000..f0b11c3ee --- /dev/null +++ b/src/QLNet/Methods/Finitedifferences/Solvers/FdmHullWhiteSolver.cs @@ -0,0 +1,65 @@ +/* + Copyright (C) 2017 Jean-Camille Tournier (jean-camille.tournier@avivainvestors.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available online at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the license for more details. +*/ + +using System; + +namespace QLNet +{ + public class FdmHullWhiteSolver : LazyObject + { + public FdmHullWhiteSolver( + Handle model, + FdmSolverDesc solverDesc, + FdmSchemeDesc schemeDesc = null) + { + solverDesc_ = solverDesc; + schemeDesc_ = schemeDesc ?? new FdmSchemeDesc().Hundsdorfer(); + model_ = model; + model_.registerWith(update); + } + + public double valueAt(double s) + { + calculate(); + return solver_.interpolateAt(s); + } + public double deltaAt(double s) { + return 0.0; + } + public double gammaAt(double s) { + return 0.0; + } + public double thetaAt(double s) { + return 0.0; + } + + protected override void performCalculations() + { + FdmHullWhiteOp op = new FdmHullWhiteOp( + solverDesc_.mesher, model_.currentLink(), 0); + + solver_ = new Fdm1DimSolver(solverDesc_, schemeDesc_, op); + } + + protected Handle model_; + protected FdmSolverDesc solverDesc_; + protected FdmSchemeDesc schemeDesc_; + protected Fdm1DimSolver solver_; + } +} diff --git a/src/QLNet/Methods/Finitedifferences/StepConditions/FdmBermudanStepCondition.cs b/src/QLNet/Methods/Finitedifferences/StepConditions/FdmBermudanStepCondition.cs index 5b53a6adc..79fa1eb8b 100644 --- a/src/QLNet/Methods/Finitedifferences/StepConditions/FdmBermudanStepCondition.cs +++ b/src/QLNet/Methods/Finitedifferences/StepConditions/FdmBermudanStepCondition.cs @@ -46,7 +46,7 @@ public FdmBermudanStepCondition(List exerciseDates, public void applyTo(object o, double t) { Vector a = (Vector) o; - if (exerciseTimes_.BinarySearch(t) != exerciseTimes_.Count) + if (exerciseTimes_.BinarySearch(t) >= 0) { FdmLinearOpLayout layout = mesher_.layout(); FdmLinearOpIterator endIter = layout.end(); diff --git a/src/QLNet/Methods/Finitedifferences/Utilities/FdmAffineModelSwapInnerValue.cs b/src/QLNet/Methods/Finitedifferences/Utilities/FdmAffineModelSwapInnerValue.cs new file mode 100644 index 000000000..15d1c5836 --- /dev/null +++ b/src/QLNet/Methods/Finitedifferences/Utilities/FdmAffineModelSwapInnerValue.cs @@ -0,0 +1,132 @@ +/* + Copyright (C) 2017 Jean-Camille Tournier (jean-camille.tournier@avivainvestors.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available online at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the license for more details. +*/ + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace QLNet +{ + public class FdmAffineModelSwapInnerValue : FdmInnerValueCalculator where ModelType : ITermStructureConsistentModel, IAffineModel + { + public FdmAffineModelSwapInnerValue( + ModelType disModel, + ModelType fwdModel, + VanillaSwap swap, + Dictionary exerciseDates, + FdmMesher mesher, + int direction) + { + disModel_ = disModel; + fwdModel_ = fwdModel; + mesher_ = mesher; + direction_ = direction; + swap_ = new VanillaSwap(swap.swapType, + swap.nominal, + swap.fixedSchedule(), + swap.fixedRate, + swap.fixedDayCount(), + swap.floatingSchedule(), + swap.iborIndex().clone(fwdTs_), + swap.spread, + swap.floatingDayCount(), + null); + exerciseDates_ = exerciseDates; + } + + public override double innerValue(FdmLinearOpIterator iter, double t) + { + Date iterExerciseDate = exerciseDates_.ContainsKey(t) ? exerciseDates_[t] : exerciseDates_.Last().Value; + + Vector disRate = getState(disModel_, t, iter); + Vector fwdRate = getState(fwdModel_, t, iter); + + if (disTs_.empty() || iterExerciseDate != disTs_.currentLink().referenceDate()) + { + Handle discount + = disModel_.termStructure(); + + disTs_.linkTo(new FdmAffineModelTermStructure(disRate, + discount.currentLink().calendar(), discount.currentLink().dayCounter(), + iterExerciseDate, discount.currentLink().referenceDate(), + disModel_)); + + Handle fwd = fwdModel_.termStructure(); + + fwdTs_.linkTo(new FdmAffineModelTermStructure(fwdRate, + fwd.currentLink().calendar(), fwd.currentLink().dayCounter(), + iterExerciseDate, fwd.currentLink().referenceDate(), + fwdModel_)); + + } + else { + (disTs_.currentLink() as FdmAffineModelTermStructure).setVariable(disRate); + (fwdTs_.currentLink() as FdmAffineModelTermStructure).setVariable(fwdRate); + } + + double npv = 0.0; + for (int j = 0; j < 2; j++) { + for (int i =0; i < swap_.leg(j).Count; ++i) { + npv += (swap_.leg(j)[i] as Coupon).accrualStartDate() >= iterExerciseDate + ? swap_.leg(j)[i].amount() * disTs_.currentLink().discount(swap_.leg(j)[i].date()) + : 0.0; + } + if (j == 0) + npv *= -1.0; + } + if (swap_.swapType == VanillaSwap.Type.Receiver) + npv *= -1.0; + + return Math.Max(0.0, npv); + } + public override double avgInnerValue(FdmLinearOpIterator iter, double t) + { + return innerValue(iter, t); + } + + public Vector getState(ModelType model, double t, FdmLinearOpIterator iter) + { + if (model.GetType().Equals(typeof(HullWhite))) + { + Vector retVal = new Vector(1, (model as HullWhite).dynamics().shortRate(t, + mesher_.location(iter, direction_))); + return retVal; + } + else if (model.GetType().Equals(typeof(G2))) + { + Vector retVal = new Vector(2); + retVal[0] = mesher_.location(iter, direction_); + retVal[1] = mesher_.location(iter, direction_ + 1); + + return retVal; + } + else + return new Vector(); + } + + protected RelinkableHandle disTs_ = new RelinkableHandle(), fwdTs_ = new RelinkableHandle(); + protected ModelType disModel_, fwdModel_; + + protected IborIndex index_; + protected VanillaSwap swap_; + protected Dictionary exerciseDates_; + protected FdmMesher mesher_; + protected int direction_; + } +} diff --git a/src/QLNet/Methods/Finitedifferences/Utilities/FdmAffineModelTermStructure.cs b/src/QLNet/Methods/Finitedifferences/Utilities/FdmAffineModelTermStructure.cs new file mode 100644 index 000000000..0b2f63f23 --- /dev/null +++ b/src/QLNet/Methods/Finitedifferences/Utilities/FdmAffineModelTermStructure.cs @@ -0,0 +1,59 @@ +/* + Copyright (C) 2017 Jean-Camille Tournier (jean-camille.tournier@avivainvestors.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available online at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the license for more details. +*/ + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace QLNet +{ + public class FdmAffineModelTermStructure : YieldTermStructure + { + public FdmAffineModelTermStructure( + Vector r, + Calendar cal, + DayCounter dayCounter, + Date referenceDate, + Date modelReferenceDate, + IAffineModel model) + : base(referenceDate, cal, dayCounter) + { + r_ = r; + t_ = dayCounter.yearFraction(modelReferenceDate, referenceDate); + model_ = model; + model_.registerWith(update); + } + + public override Date maxDate() { return Date.maxDate(); } + public void setVariable(Vector r) + { + r_ = r; + notifyObservers(); + } + + protected override double discountImpl(double d) + { + return model_.discountBond(t_, d + t_, r_); + } + + protected Vector r_; + protected double t_; + protected IAffineModel model_; + } +} diff --git a/src/QLNet/Methods/Finitedifferences/finitedifferencemodel.cs b/src/QLNet/Methods/Finitedifferences/finitedifferencemodel.cs index d7bdd919a..31cfff9c4 100644 --- a/src/QLNet/Methods/Finitedifferences/finitedifferencemodel.cs +++ b/src/QLNet/Methods/Finitedifferences/finitedifferencemodel.cs @@ -67,6 +67,7 @@ private void rollbackImpl(ref object o, double from, double to, int steps, IStep } for (int i=0; i= 0 ; --j) { if (next <= stoppingTimes_[j] && stoppingTimes_[j] < now) { diff --git a/src/QLNet/Pricingengines/vanilla/FdHullWhiteSwaptionEngine.cs b/src/QLNet/Pricingengines/vanilla/FdHullWhiteSwaptionEngine.cs new file mode 100644 index 000000000..b03436402 --- /dev/null +++ b/src/QLNet/Pricingengines/vanilla/FdHullWhiteSwaptionEngine.cs @@ -0,0 +1,117 @@ +/* + Copyright (C) 2017 Jean-Camille Tournier (jean-camille.tournier@avivainvestors.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available online at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the license for more details. +*/ + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace QLNet +{ + public class FdHullWhiteSwaptionEngine : GenericModelEngine + { + public FdHullWhiteSwaptionEngine( + HullWhite model, + int tGrid = 100, int xGrid = 100, + int dampingSteps = 0, double invEps = 1e-5, + FdmSchemeDesc schemeDesc = null) + : base(model) + { + tGrid_ = tGrid; + xGrid_ = xGrid; + dampingSteps_ = dampingSteps; + schemeDesc_ = schemeDesc == null ? new FdmSchemeDesc().Douglas() : schemeDesc; + invEps_ = invEps; + } + + public override void calculate() { + // 1. Term structure + Handle ts = model_.currentLink().termStructure(); + + // 2. Mesher + DayCounter dc = ts.currentLink().dayCounter(); + Date referenceDate = ts.currentLink().referenceDate(); + double maturity = dc.yearFraction(referenceDate, + arguments_.exercise.lastDate()); + + + OrnsteinUhlenbeckProcess process = new OrnsteinUhlenbeckProcess(model_.currentLink().a(), model_.currentLink().sigma()); + + Fdm1dMesher shortRateMesher = + new FdmSimpleProcess1DMesher(xGrid_, process, maturity, 1, invEps_); + + FdmMesher mesher = new FdmMesherComposite(shortRateMesher); + + // 3. Inner Value Calculator + List exerciseDates = arguments_.exercise.dates(); + Dictionary t2d = new Dictionary(); + + for (int i=0; i < exerciseDates.Count; ++i) { + double t = dc.yearFraction(referenceDate, exerciseDates[i]); + Utils.QL_REQUIRE(t >= 0, () => "exercise dates must not contain past date"); + + t2d.Add(t, exerciseDates[i]); + } + + Handle disTs = model_.currentLink().termStructure(); + Handle fwdTs + = arguments_.swap.iborIndex().forwardingTermStructure(); + + Utils.QL_REQUIRE(fwdTs.currentLink().dayCounter() == disTs.currentLink().dayCounter(), + () => "day counter of forward and discount curve must match"); + Utils.QL_REQUIRE(fwdTs.currentLink().referenceDate() == disTs.currentLink().referenceDate(), + () => "reference date of forward and discount curve must match"); + + HullWhite fwdModel = + new HullWhite(fwdTs, model_.currentLink().a(), model_.currentLink().sigma()); + + FdmInnerValueCalculator calculator = + new FdmAffineModelSwapInnerValue( + model_.currentLink(), fwdModel, + arguments_.swap, t2d, mesher, 0); + + // 4. Step conditions + FdmStepConditionComposite conditions = + FdmStepConditionComposite.vanillaComposite( + new DividendSchedule(), arguments_.exercise, + mesher, calculator, referenceDate, dc); + + // 5. Boundary conditions + FdmBoundaryConditionSet boundaries = new FdmBoundaryConditionSet(); + + // 6. Solver + FdmSolverDesc solverDesc = new FdmSolverDesc(); + solverDesc.mesher = mesher; + solverDesc.bcSet = boundaries; + solverDesc.condition = conditions; + solverDesc.calculator = calculator; + solverDesc.maturity = maturity; + solverDesc.timeSteps = tGrid_; + solverDesc.dampingSteps = dampingSteps_; + + FdmHullWhiteSolver solver = + new FdmHullWhiteSolver(model_, solverDesc, schemeDesc_); + + results_.value = solver.valueAt(0.0); + } + + protected int tGrid_, xGrid_, dampingSteps_; + protected FdmSchemeDesc schemeDesc_; + protected double invEps_; + } +} diff --git a/tests/QLNet.Tests/T_Bermudanswaption.cs b/tests/QLNet.Tests/T_Bermudanswaption.cs index b69dd87b2..44e0489c6 100644 --- a/tests/QLNet.Tests/T_Bermudanswaption.cs +++ b/tests/QLNet.Tests/T_Bermudanswaption.cs @@ -164,37 +164,57 @@ public void testCachedValues() { } Exercise exercise = new BermudanExercise(exerciseDates); - IPricingEngine engine = new TreeSwaptionEngine(model, 50); + IPricingEngine treeEngine = new TreeSwaptionEngine(model, 50); + IPricingEngine fdmEngine = new FdHullWhiteSwaptionEngine(model as HullWhite); #if QL_USE_INDEXED_COUPON - Real itmValue = 42.2413, atmValue = 12.8789, otmValue = 2.4759; - #else - double itmValue = 42.2470, atmValue = 12.8826, otmValue = 2.4769; + double itmValue = 42.2413, atmValue = 12.8789, otmValue = 2.4759; + double itmValueFdm = 42.2111, atmValueFdm = 12.8879, otmValueFdm = 2.44443; + #else + double itmValue = 42.2470, atmValue = 12.8826, otmValue = 2.4769; + double itmValueFdm = 42.2091, atmValueFdm = 12.8864, otmValueFdm = 2.4437; #endif double tolerance = 1.0e-4; Swaption swaption = new Swaption(itmSwap, exercise); - swaption.setPricingEngine(engine); + swaption.setPricingEngine(treeEngine); if (Math.Abs(swaption.NPV()-itmValue) > tolerance) QAssert.Fail("failed to reproduce cached in-the-money swaption value:\n" + "calculated: " + swaption.NPV() + "\n" - + "expected: " + itmValue); + + "expected: " + itmValue); + + swaption.setPricingEngine(fdmEngine); + if (Math.Abs(swaption.NPV() - itmValueFdm) > tolerance) + QAssert.Fail("failed to reproduce cached in-the-money swaption value:\n" + + "calculated: " + swaption.NPV() + "\n" + + "expected: " + itmValueFdm); - swaption = new Swaption(atmSwap, exercise); - swaption.setPricingEngine(engine); + swaption = new Swaption(atmSwap, exercise); + swaption.setPricingEngine(treeEngine); if (Math.Abs(swaption.NPV()-atmValue) > tolerance) QAssert.Fail("failed to reproduce cached at-the-money swaption value:\n" + "calculated: " + swaption.NPV() + "\n" - + "expected: " + atmValue); + + "expected: " + atmValue); + swaption.setPricingEngine(fdmEngine); + if (Math.Abs(swaption.NPV() - atmValueFdm) > tolerance) + QAssert.Fail("failed to reproduce cached at-the-money swaption value:\n" + + "calculated: " + swaption.NPV() + "\n" + + "expected: " + atmValueFdm); - swaption = new Swaption(otmSwap, exercise); - swaption.setPricingEngine(engine); + swaption = new Swaption(otmSwap, exercise); + swaption.setPricingEngine(treeEngine); if (Math.Abs(swaption.NPV()-otmValue) > tolerance) QAssert.Fail("failed to reproduce cached out-of-the-money " + "swaption value:\n" + "calculated: " + swaption.NPV() + "\n" - + "expected: " + otmValue); + + "expected: " + otmValue); + swaption.setPricingEngine(fdmEngine); + if (Math.Abs(swaption.NPV() - otmValueFdm) > tolerance) + QAssert.Fail("failed to reproduce cached out-of-the-money " + + "swaption value:\n" + + "calculated: " + swaption.NPV() + "\n" + + "expected: " + otmValueFdm); for (int j=0; j tolerance) QAssert.Fail("failed to reproduce cached in-the-money swaption value:\n" + "calculated: " + swaption.NPV() + "\n" + "expected: " + itmValue); - swaption = new Swaption(atmSwap, exercise); - swaption.setPricingEngine(engine); + swaption = new Swaption(atmSwap, exercise); + swaption.setPricingEngine(treeEngine); if (Math.Abs(swaption.NPV()-atmValue) > tolerance) QAssert.Fail("failed to reproduce cached at-the-money swaption value:\n" + "calculated: " + swaption.NPV() + "\n" + "expected: " + atmValue); - swaption = new Swaption(otmSwap, exercise); - swaption.setPricingEngine(engine); + swaption = new Swaption(otmSwap, exercise); + swaption.setPricingEngine(treeEngine); if (Math.Abs(swaption.NPV()-otmValue) > tolerance) QAssert.Fail("failed to reproduce cached out-of-the-money " + "swaption value:\n"