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"