public void testTripleBandMapSolve() { int[] dims = new int[] { 100, 400 }; List <int> dim = new List <int>(dims); FdmLinearOpLayout layout = new FdmLinearOpLayout(dim); List <Pair <double?, double?> > boundaries = new List <Pair <double?, double?> > (); boundaries.Add(new Pair <double?, double?>(0, 1.0)); boundaries.Add(new Pair <double?, double?>(0, 1.0)); FdmMesher mesher = new UniformGridMesher(layout, boundaries); FirstDerivativeOp dy = new FirstDerivativeOp(1, mesher); dy.axpyb(new Vector(1, 2.0), dy, dy, new Vector(1, 1.0)); // check copy constructor FirstDerivativeOp copyOfDy = new FirstDerivativeOp(dy); Vector u = new Vector(layout.size()); for (int i = 0; i < layout.size(); ++i) { u[i] = Math.Sin(0.1 * i) + Math.Cos(0.35 * i); } Vector t = new Vector(dy.solve_splitting(copyOfDy.apply(u), 1.0, 0.0)); for (int i = 0; i < u.size(); ++i) { if (Math.Abs(u[i] - t[i]) > 1e-6) { QAssert.Fail("solve and apply are not consistent " + "\n expected : " + u[i] + "\n calculated : " + t[i]); } } FirstDerivativeOp dx = new FirstDerivativeOp(0, mesher); dx.axpyb(new Vector(), dx, dx, new Vector(1, 1.0)); FirstDerivativeOp copyOfDx = new FirstDerivativeOp(0, mesher); // check assignment copyOfDx = dx; t = dx.solve_splitting(copyOfDx.apply(u), 1.0, 0.0); for (int i = 0; i < u.size(); ++i) { if (Math.Abs(u[i] - t[i]) > 1e-6) { QAssert.Fail("solve and apply are not consistent " + "\n expected : " + u[i] + "\n calculated : " + t[i]); } } SecondDerivativeOp dxx = new SecondDerivativeOp(0, mesher); dxx.axpyb(new Vector(1, 0.5), dxx, dx, new Vector(1, 1.0)); // check of copy constructor SecondDerivativeOp copyOfDxx = new SecondDerivativeOp(dxx); t = dxx.solve_splitting(copyOfDxx.apply(u), 1.0, 0.0); for (int i = 0; i < u.size(); ++i) { if (Math.Abs(u[i] - t[i]) > 1e-6) { QAssert.Fail("solve and apply are not consistent " + "\n expected : " + u[i] + "\n calculated : " + t[i]); } } //check assignment operator copyOfDxx.add(new SecondDerivativeOp(1, mesher)); copyOfDxx = dxx; t = dxx.solve_splitting(copyOfDxx.apply(u), 1.0, 0.0); for (int i = 0; i < u.size(); ++i) { if (Math.Abs(u[i] - t[i]) > 1e-6) { QAssert.Fail("solve and apply are not consistent " + "\n expected : " + u[i] + "\n calculated : " + t[i]); } } }
public void testCrankNicolsonWithDamping() { SavedSettings backup = new SavedSettings(); DayCounter dc = new Actual360(); Date today = Date.Today; SimpleQuote spot = new SimpleQuote(100.0); YieldTermStructure qTS = Utilities.flatRate(today, 0.06, dc); YieldTermStructure rTS = Utilities.flatRate(today, 0.06, dc); BlackVolTermStructure volTS = Utilities.flatVol(today, 0.35, dc); StrikedTypePayoff payoff = new CashOrNothingPayoff(Option.Type.Put, 100, 10.0); double maturity = 0.75; Date exDate = today + Convert.ToInt32(maturity * 360 + 0.5); Exercise exercise = new EuropeanExercise(exDate); BlackScholesMertonProcess process = new BlackScholesMertonProcess(new Handle <Quote>(spot), new Handle <YieldTermStructure>(qTS), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS)); IPricingEngine engine = new AnalyticEuropeanEngine(process); VanillaOption opt = new VanillaOption(payoff, exercise); opt.setPricingEngine(engine); double expectedPV = opt.NPV(); double expectedGamma = opt.gamma(); // fd pricing using implicit damping steps and Crank Nicolson int csSteps = 25, dampingSteps = 3, xGrid = 400; List <int> dim = new InitializedList <int>(1, xGrid); FdmLinearOpLayout layout = new FdmLinearOpLayout(dim); Fdm1dMesher equityMesher = new FdmBlackScholesMesher( dim[0], process, maturity, payoff.strike(), null, null, 0.0001, 1.5, new Pair <double?, double?>(payoff.strike(), 0.01)); FdmMesher mesher = new FdmMesherComposite(equityMesher); FdmBlackScholesOp map = new FdmBlackScholesOp(mesher, process, payoff.strike()); FdmInnerValueCalculator calculator = new FdmLogInnerValue(payoff, mesher, 0); object rhs = new Vector(layout.size()); Vector x = new Vector(layout.size()); FdmLinearOpIterator endIter = layout.end(); for (FdmLinearOpIterator iter = layout.begin(); iter != endIter; ++iter) { (rhs as Vector)[iter.index()] = calculator.avgInnerValue(iter, maturity); x[iter.index()] = mesher.location(iter, 0); } FdmBackwardSolver solver = new FdmBackwardSolver(map, new FdmBoundaryConditionSet(), new FdmStepConditionComposite(), new FdmSchemeDesc().Douglas()); solver.rollback(ref rhs, maturity, 0.0, csSteps, dampingSteps); MonotonicCubicNaturalSpline spline = new MonotonicCubicNaturalSpline(x, x.Count, rhs as Vector); double s = spot.value(); double calculatedPV = spline.value(Math.Log(s)); double calculatedGamma = (spline.secondDerivative(Math.Log(s)) - spline.derivative(Math.Log(s))) / (s * s); double relTol = 2e-3; if (Math.Abs(calculatedPV - expectedPV) > relTol * expectedPV) { QAssert.Fail("Error calculating the PV of the digital option" + "\n rel. tolerance: " + relTol + "\n expected: " + expectedPV + "\n calculated: " + calculatedPV); } if (Math.Abs(calculatedGamma - expectedGamma) > relTol * expectedGamma) { QAssert.Fail("Error calculating the Gamma of the digital option" + "\n rel. tolerance: " + relTol + "\n expected: " + expectedGamma + "\n calculated: " + calculatedGamma); } }
public void testFdmLinearOpLayout() { int[] dims = new int[] { 5, 7, 8 }; List <int> dim = new List <int>(dims); FdmLinearOpLayout layout = new FdmLinearOpLayout(dim); int calculatedDim = layout.dim().Count; int expectedDim = dim.Count; if (calculatedDim != expectedDim) { QAssert.Fail("index.dimensions() should be " + expectedDim + ", but is " + calculatedDim); } int calculatedSize = layout.size(); int expectedSize = dim.accumulate(0, 3, 1, (x, y) => (x * y)); if (calculatedSize != expectedSize) { QAssert.Fail("index.size() should be " + expectedSize + ", but is " + calculatedSize); } for (int k = 0; k < dim[0]; ++k) { for (int l = 0; l < dim[1]; ++l) { for (int m = 0; m < dim[2]; ++m) { List <int> tmp = new InitializedList <int>(3); tmp[0] = k; tmp[1] = l; tmp[2] = m; int calculatedIndex = layout.index(tmp); int expectedIndex = k + l * dim[0] + m * dim[0] * dim[1]; if (expectedIndex != layout.index(tmp)) { QAssert.Fail("index.size() should be " + expectedIndex + ", but is " + calculatedIndex); } } } } FdmLinearOpIterator iter = layout.begin(); for (int m = 0; m < dim[2]; ++m) { for (int l = 0; l < dim[1]; ++l) { for (int k = 0; k < dim[0]; ++k, ++iter) { for (int n = 1; n < 4; ++n) { int nn = layout.neighbourhood(iter, 1, n); int calculatedIndex = k + m * dim[0] * dim[1] + ((l < dim[1] - n)? l + n : dim[1] - 1 - (l + n - (dim[1] - 1))) * dim[0]; if (nn != calculatedIndex) { QAssert.Fail("next neighbourhood index is " + nn + " but should be " + calculatedIndex); } } for (int n = 1; n < 7; ++n) { int nn = layout.neighbourhood(iter, 2, -n); int calculatedIndex = k + l * dim[0] + ((m < n) ? n - m : m - n) * dim[0] * dim[1]; if (nn != calculatedIndex) { QAssert.Fail("next neighbourhood index is " + nn + " but should be " + calculatedIndex); } } } } } }