@Test public void test() { assertEquals( Math.log(INTERPOLATOR.interpolate(MODEL, 3.4)), LINEAR.interpolate(TRANSFORMED_MODEL, 3.4), EPS); }
/** * Prices a log-contract for a given local volatility surface by backwards solving the PDE * expressed in terms of (the log of) the real stock price - this requires having jumps conditions * in the PDE * * @param lv Local volatility * @return Forward (non-discounted) price of log-contact */ private double logContractPriceFromSpotPDE(final LocalVolatilitySurfaceStrike lv) { // Set up the PDE to give the forward (non-discounted) option price final ConvectionDiffusionPDE1DCoefficients pde = PDE_PROVIDER.getLogBackwardsLocalVol(0.0, -DRIFT, EXPIRY, lv); final Function1D<Double, Double> initialCon = INITIAL_COND_PROVIDER.getLogContractPayoffInLogCoordinate(); final double theta = 0.5; final double yL = Math.log(SPOT / 6); final double yH = Math.log(6 * SPOT); final ConvectionDiffusionPDESolver solver = new ThetaMethodFiniteDifference(theta, false); final BoundaryCondition lower1 = new NeumannBoundaryCondition(1.0, yL, true); final BoundaryCondition upper1 = new NeumannBoundaryCondition(1.0, yH, false); final MeshingFunction timeMesh1 = new ExponentialMeshing(0, EXPIRY - DIVIDEND_DATE - 1e-6, 50, 0.0); final MeshingFunction timeMesh2 = new ExponentialMeshing(EXPIRY - DIVIDEND_DATE + 1e-6, EXPIRY, 50, 0.0); final MeshingFunction spaceMesh = new ExponentialMeshing(yL, yH, 101, 0.0); final PDEGrid1D grid1 = new PDEGrid1D(timeMesh1, spaceMesh); final double[] sNodes1 = grid1.getSpaceNodes(); // run the PDE solver backward to the dividend date final PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients> db1 = new PDE1DDataBundle<>(pde, initialCon, lower1, upper1, grid1); final PDETerminalResults1D res1 = (PDETerminalResults1D) solver.solve(db1); // Map the spot nodes after (in calendar time) the dividend payment to nodes before final int nSNodes = sNodes1.length; final double[] sNodes2 = new double[nSNodes]; final double lnBeta = Math.log(1 - BETA); for (int i = 0; i < nSNodes; i++) { final double temp = sNodes1[i]; if (temp < 0) { sNodes2[i] = Math.log(Math.exp(temp) + ALPHA) - lnBeta; } else { sNodes2[i] = temp + Math.log(1 + ALPHA * Math.exp(-temp)) - lnBeta; } } final PDEGrid1D grid2 = new PDEGrid1D(timeMesh2.getPoints(), sNodes2); final BoundaryCondition lower2 = new NeumannBoundaryCondition(1.0, sNodes2[0], true); final BoundaryCondition upper2 = new NeumannBoundaryCondition(1.0, sNodes2[nSNodes - 1], false); // run the PDE solver backward from the dividend date to zero final PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients> db2 = new PDE1DDataBundle<>(pde, res1.getTerminalResults(), lower2, upper2, grid2); final PDETerminalResults1D res2 = (PDETerminalResults1D) solver.solve(db2); final Interpolator1DDataBundle interpolDB2 = INTEPOLATOR1D.getDataBundle(sNodes2, res2.getTerminalResults()); final double val2 = INTEPOLATOR1D.interpolate(interpolDB2, Math.log(SPOT)); return val2; }
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 }
/** * Check the the log-contract is correctly prices using a backwards PDE expressed in terms of (the * log of) the forward F(t,T) - this requires NO jumps conditions in the PDE */ @Test public void backwardsDebugPDEtest() { final double fT = DIV_CURVES.getF(EXPIRY); final double lnFT = Math.log(fT); final Function1D<Double, Double> payoff = new Function1D<Double, Double>() { @Override public Double evaluate(final Double y) { return y - lnFT; } }; // ZZConvectionDiffusionPDEDataBundle pdeBundle1 = getBackwardsPDEDataBundle(EXPIRY, LOCAL_VOL, // payoff); // ConvectionDiffusionPDE1DCoefficients pde = // PDE_PROVIDER.getLogBackwardsLocalVol(FORWARD_CURVE, EXPIRY, LOCAL_VOL); final ConvectionDiffusionPDE1DCoefficients pde = PDE_PROVIDER.getLogBackwardsLocalVol(0.0, 0.0, EXPIRY, LOCAL_VOL_SPECIAL); final double theta = 0.5; final double range = Math.log(5); final double yL = lnFT - range; final double yH = lnFT + range; final ConvectionDiffusionPDESolver solver = new ThetaMethodFiniteDifference(theta, false); final BoundaryCondition lower = new NeumannBoundaryCondition(1.0, yL, true); final BoundaryCondition upper = new NeumannBoundaryCondition(1.0, yH, false); final MeshingFunction timeMesh = new ExponentialMeshing(0, EXPIRY, 100, 0.0); final MeshingFunction spaceMesh = new ExponentialMeshing(yL, yH, 101, 0.0); final PDEGrid1D grid = new PDEGrid1D(timeMesh, spaceMesh); final double[] sNodes = grid.getSpaceNodes(); // run the PDE solver backward to the dividend date // PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients> db1 = new // PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients>(pde, initialCon, lower1, upper1, // grid1); final PDE1DDataBundle<ConvectionDiffusionPDE1DCoefficients> db1 = new PDE1DDataBundle<>(pde, payoff, lower, upper, grid); final PDETerminalResults1D res = (PDETerminalResults1D) solver.solve(db1); final Interpolator1DDataBundle interpolDB = INTEPOLATOR1D.getDataBundle(sNodes, res.getTerminalResults()); final double val = INTEPOLATOR1D.interpolate(interpolDB, lnFT); assertEquals(0.41491529, Math.sqrt(-2 * (val) / EXPIRY), 5e-4); // Number from backwardsPDETest // System.out.println(val + "\t" + Math.sqrt(-2 * val / EXPIRY)); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testHighValue() { INTERPOLATOR.interpolate(MODEL, 12.); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNullData() { INTERPOLATOR.interpolate(MODEL, null); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNullDataBundle() { INTERPOLATOR.interpolate(null, 3.4); }