private double logContactPriceFromPureSpot(final LocalVolatilitySurfaceMoneyness lv) { final double fT = DIV_CURVES.getF(EXPIRY); final double dT = DIV_CURVES.getD(EXPIRY); final double dStar = dT / (fT - dT); final ConvectionDiffusionPDE1DCoefficients pde = PDE_PROVIDER.getLogBackwardsLocalVol(EXPIRY, lv); final double theta = 0.5; final double yL = -0.5; final double yH = 0.5; final ConvectionDiffusionPDESolver solver = new ThetaMethodFiniteDifference(theta, false); final BoundaryCondition lower = new NeumannBoundaryCondition(1 / (1 + dStar * Math.exp(-yL)), yL, true); final BoundaryCondition upper = new NeumannBoundaryCondition(1.0, yH, false); final MeshingFunction timeMesh = new ExponentialMeshing(0.0, EXPIRY, 100, 0.0); final MeshingFunction spaceMesh = new ExponentialMeshing(yL, yH, 101, 0.0); final PDEGrid1D grid = new PDEGrid1D(timeMesh, spaceMesh); final PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients> db = new PDE1DDataBundle<>(pde, PURE_LOG_PAY_OFF, lower, upper, grid); final PDEResults1D res = solver.solve(db); final int n = res.getNumberSpaceNodes(); final double val = res.getFunctionValue(n / 2); return val; }
public void testMixedLogNormalVolSurface() { final double[] weights = new double[] {0.9, 0.1}; final double[] sigmas = new double[] {0.2, 0.8}; final MultiHorizonMixedLogNormalModelData data = new MultiHorizonMixedLogNormalModelData(weights, sigmas); final LocalVolatilitySurfaceStrike lv = MixedLogNormalVolatilitySurface.getLocalVolatilitySurface(FORWARD_CURVE, data); final LocalVolatilitySurfaceMoneyness lvm = LocalVolatilitySurfaceConverter.toMoneynessSurface(lv, FORWARD_CURVE); final double expected = Math.sqrt(weights[0] * sigmas[0] * sigmas[0] + weights[1] * sigmas[1] * sigmas[1]); final double ft = FORWARD_CURVE.getForward(EXPIRY); final double theta = 0.5; // Review the accuracy is very dependent on these numbers final double fL = Math.log(ft / 30); final double fH = Math.log(30 * ft); final ConvectionDiffusionPDESolver solver = new ThetaMethodFiniteDifference(theta, false); final BoundaryCondition lower = new NeumannBoundaryCondition(1.0, fL, true); final BoundaryCondition upper = new NeumannBoundaryCondition(1.0, fH, false); final MeshingFunction timeMesh = new ExponentialMeshing(0, EXPIRY, 50, 0.0); final MeshingFunction spaceMesh = new HyperbolicMeshing(fL, fH, (fL + fH) / 2, 101, 0.4); final ConvectionDiffusionPDE1DStandardCoefficients pde = PDE_DATA_PROVIDER.getLogBackwardsLocalVol(EXPIRY, lvm); final PDEGrid1D grid = new PDEGrid1D(timeMesh, spaceMesh); final PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients> db = new PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients>( pde, INITIAL_COND, lower, upper, grid); final PDEResults1D res = solver.solve(db); final int n = res.getNumberSpaceNodes(); final double[] values = new double[n]; for (int i = 0; i < n; i++) { values[i] = res.getFunctionValue(i); } final Interpolator1DDataBundle idb = INTERPOLATOR.getDataBundle(grid.getSpaceNodes(), values); final double elogS = INTERPOLATOR.interpolate(idb, Math.log(ft)); final double kVol = Math.sqrt(-2 * (elogS - Math.log(ft)) / EXPIRY); assertEquals( expected, kVol, 1e-3); // TODO Improve on 10bps error - local surface is (by construction) very smooth. // NOTE: this has got worse since we improved the T -> 0 // behaviour of the mixed log-normal local volatility surface }
public void testFlatSurface() { final double theta = 0.5; final double ft = FORWARD_CURVE.getForward(EXPIRY); final double fL = Math.log(ft / 5.0); final double fH = Math.log(5.0 * ft); final ConvectionDiffusionPDESolver solver = new ThetaMethodFiniteDifference(theta, false); final BoundaryCondition lower = new NeumannBoundaryCondition(1.0, fL, true); final BoundaryCondition upper = new NeumannBoundaryCondition(1.0, fH, false); final MeshingFunction timeMesh = new ExponentialMeshing(0, EXPIRY, 100, 0.0); final MeshingFunction spaceMesh = new ExponentialMeshing(fL, fH, 101, 0.0); final PDEGrid1D grid = new PDEGrid1D(timeMesh, spaceMesh); final PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients> db = new PDE1DDataBundle<>(PDE, INITIAL_COND, lower, upper, grid); final PDEResults1D res = solver.solve(db); final int n = res.getNumberSpaceNodes(); final double kVol = Math.sqrt(-2 * (res.getFunctionValue(n / 2) - Math.log(ft)) / EXPIRY); assertEquals(FLAT_VOL, kVol, 1e-6); final YieldAndDiscountCurve yieldCurve = new YieldCurve("test", ConstantDoublesCurve.from(DRIFT)); final AffineDividends ad = AffineDividends.noDividends(); final EquityVarianceSwapBackwardsPurePDE backSolver = new EquityVarianceSwapBackwardsPurePDE(); final PureLocalVolatilitySurface plv = new PureLocalVolatilitySurface(ConstantDoublesSurface.from(FLAT_VOL)); final double[] res2 = backSolver.expectedVariance(SPOT, yieldCurve, ad, EXPIRY, plv); final double kVol2 = Math.sqrt(res2[0] / EXPIRY); assertEquals(FLAT_VOL, kVol2, 1e-6); }