LocalVolatilityBackwardsPDEPricer { public double price(final ForwardCurve fwd, final Curve<Double, Double> riskFreeRate, final EuropeanVanillaOption option, final LocalVolatilitySurfaceStrike localVol, final boolean isAmerican, final int spaceNodes, final int timeNodes) { final double t = option.getTimeToExpiry(); final double s0 = fwd.getSpot(); final double k = option.getStrike(); final double sigma = Math.max(localVol.getVolatility(t, k), localVol.getVolatility(t, s0)); final double mult = Math.exp(6.0 * sigma * Math.sqrt(t)); final double sMin = Math.min(0.8 * k, s0 / mult); final double sMax = Math.max(1.25 * k, s0 * mult); final double[] fixedPoints = k == 0.0 ? new double[] {s0} : new double[] {s0, k}; final MeshingFunction xMesh = new ExponentialMeshing(sMin, sMax, spaceNodes, 0.0, fixedPoints); PDEGrid1D[] grid; double[] theta; if (_useBurnin) { final int tBurnNodes = (int) Math.max(2, timeNodes * _burninFrac); final double tBurn = _burninFrac * t * t / timeNodes; if (tBurn >= t) { final int minNodes = (int) Math.ceil(_burninFrac * t); final double minFrac = timeNodes / t; throw new IllegalArgumentException("burn in period greater than total time. Either increase timeNodes to above " + minNodes + ", or reduce burninFrac to below " + minFrac); } final MeshingFunction tBurnMesh = new ExponentialMeshing(0.0, tBurn, tBurnNodes, 0.0); final MeshingFunction tMesh = new ExponentialMeshing(tBurn, t, timeNodes - tBurnNodes, 0.0); grid = new PDEGrid1D[2]; grid[0] = new PDEGrid1D(tBurnMesh, xMesh); grid[1] = new PDEGrid1D(tMesh, xMesh); theta = new double[] {_burninTheta, _mainRunTheta}; } else { grid = new PDEGrid1D[1]; final MeshingFunction tMesh = new ExponentialMeshing(0, t, timeNodes, 0.0); grid[0] = new PDEGrid1D(tMesh, xMesh); theta = new double[] {_mainRunTheta}; } return price(fwd, riskFreeRate, option, localVol, isAmerican, grid, theta); } LocalVolatilityBackwardsPDEPricer();  LocalVolatilityBackwardsPDEPricer(final boolean useBurnin);  LocalVolatilityBackwardsPDEPricer(final boolean useBurnin, final double burninFrac);  LocalVolatilityBackwardsPDEPricer(final boolean useBurnin, final double burninFrac, final double burninTheta, final double mainTheta); double price(final ForwardCurve fwd, final Curve<Double, Double> riskFreeRate, final EuropeanVanillaOption option, final LocalVolatilitySurfaceStrike localVol, final boolean isAmerican,
      final int spaceNodes, final int timeNodes); double price(final ForwardCurve fwd, final Curve<Double, Double> riskFreeRate, final EuropeanVanillaOption option, final LocalVolatilitySurfaceStrike localVol, final boolean isAmerican,
      final int spaceNodes, final int timeNodes, final double beta, final double lambda, final double sd); double price(final ForwardCurve fwd, final Curve<Double, Double> riskFreeRate, final EuropeanVanillaOption option, final LocalVolatilitySurfaceStrike localVol, final boolean isAmerican,
      final PDEGrid1D[] grid, final double[] theta);  }
@Test public void volTermStructureTest() { final double k = 14.0; final boolean isCall = true; final int tNodes = 150; int nu = 80; int xNodes = nu * tNodes; final double df = DIS_CURVE.getDiscountFactor(T); final double fwd = FWD_CURVE.getForward(T); final double bsPrice = df * BlackFormulaRepository.price(fwd, k, T, SIGMA_HAT, isCall); final EuropeanVanillaOption option = new EuropeanVanillaOption(k, T, isCall); double pdePrice = PRICER.price(FWD_CURVE, RISK_FREE_CURVE, option, LOCAL_VOL_SUR, isCall, xNodes, tNodes); assertEquals(bsPrice, pdePrice, 1e-5 * bsPrice); nu = 4; xNodes = nu * tNodes; pdePrice = PRICER.price(FWD_CURVE, RISK_FREE_CURVE, option, LOCAL_VOL_SUR, isCall, xNodes, tNodes, 0.1, 0.0, 5.0); assertEquals(bsPrice, pdePrice, 1e-5 * bsPrice); }
@Test public void cevTest() { final CEVPriceFunction cev = new CEVPriceFunction(); final double k = 14.0; final boolean isCall = true; final int tNodes = 100; int nu = 80; int xNodes = nu * tNodes; final double df = DIS_CURVE.getDiscountFactor(T); final double fwd = FWD_CURVE.getForward(T); final double sigma = 1.0; final double beta = 0.5; final Function<Double, Double> vol = new Function2D<Double, Double>() { @Override public Double evaluate(final Double t, final Double s) { final double tau = T - t; final double rt = Math.exp(tau * R); return sigma * Math.pow(rt * s, beta - 1.0); } }; final LocalVolatilitySurfaceStrike volSurf = new LocalVolatilitySurfaceStrike(FunctionalDoublesSurface.from(vol)); final EuropeanVanillaOption option = new EuropeanVanillaOption(k, T, isCall); final CEVFunctionData data = new CEVFunctionData(fwd, df, sigma, beta); final Function1D<CEVFunctionData, Double> priceFunc = cev.getPriceFunction(option); final double cevPrice = priceFunc.evaluate(data); double pdePrice = PRICER.price(FWD_CURVE, RISK_FREE_CURVE, option, volSurf, isCall, xNodes, tNodes); assertEquals(cevPrice, pdePrice, 1e-5 * cevPrice); nu = 15; xNodes = nu * tNodes; pdePrice = PRICER.price(FWD_CURVE, RISK_FREE_CURVE, option, volSurf, isCall, xNodes, tNodes, 0.1, 0.0, 4.0); assertEquals(cevPrice, pdePrice, 1e-5 * cevPrice); }
@Test public void mixedLogNormalTest() { final double[] w = new double[] {0.7, 0.25, 0.05 }; final double[] sigma = new double[] {0.3, 0.6, 1.0 }; final double[] mu = new double[] {0.0, 0.3, -0.5 }; final MultiHorizonMixedLogNormalModelData data = new MultiHorizonMixedLogNormalModelData(w, sigma, mu); final PriceSurface priceSurf = MixedLogNormalVolatilitySurface.getPriceSurface(FWD_CURVE, DIS_CURVE, data); final LocalVolatilitySurfaceStrike locVol = MixedLogNormalVolatilitySurface.getLocalVolatilitySurface(FWD_CURVE, data); final double k = 14.0; final boolean isCall = true; final int tNodes = 50; final int nu = 20; final int xNodes = nu * tNodes; final EuropeanVanillaOption option = new EuropeanVanillaOption(k, T, isCall); final double pdePrice = PRICER.price(FWD_CURVE, RISK_FREE_CURVE, option, locVol, isCall, xNodes, tNodes, 0.05, 0.0, 8.0); final double mlnPrice = priceSurf.getPrice(T, k); assertEquals(mlnPrice, pdePrice, 5e-3 * mlnPrice); }