@Test public void testGetters() { assertEquals(SURFACE.getName(), NAME1); assertEquals(SURFACE.getFunction(), F1); assertEquals(SURFACE.getZValue(1., 2.), F1.evaluate(1., 2.), 0); assertEquals(SURFACE.getZValue(DoublesPair.of(1., 4.)), F1.evaluate(1., 4.), 0); }
/** * Get the coefficients (a, b and c) for the PDE governing the evolution of the transition density * for a single underlying (i.e. 1 spatial dimension). The PDE is of the form $frac{\partial * V}{\partial t} + a(x,t)\frac{\partial^2V}{\partial x^2}+b(x,t)\frac{\partial V}{\partial x} * +c(x,t)V=0$ where $V(x,t)$ is the density * * @param forward the forward curve * @param localVol the local volatility surface (parameterised by strike) * @return The coefficients a, b & c - which are all functions of time and asset value (space) */ public static ConvectionDiffusionPDE1DCoefficients getStandardCoefficients( final ForwardCurve forward, final LocalVolatilitySurfaceStrike localVol) { final Function<Double, Double> a = new Function<Double, Double>() { @Override public Double evaluate(final Double... ts) { ArgChecker.isTrue(ts.length == 2); final double t = ts[0]; final double s = ts[1]; final double sigma = localVol.getVolatility(t, s) * s; return -0.5 * sigma * sigma; } }; final Function<Double, Double> b = new Function<Double, Double>() { @SuppressWarnings("synthetic-access") @Override public Double evaluate(final Double... ts) { ArgChecker.isTrue(ts.length == 2); final double t = ts[0]; final double s = ts[1]; final double lvDiv = getLocalVolFirstDiv(localVol, t, s); final double lv = localVol.getVolatility(t, s); return s * (forward.getDrift(t) - 2 * lv * (s * lvDiv + lv)); } }; final Function<Double, Double> c = new Function<Double, Double>() { @SuppressWarnings("synthetic-access") @Override public Double evaluate(final Double... ts) { ArgChecker.isTrue(ts.length == 2); final double t = ts[0]; final double s = ts[1]; final double lv1Div = getLocalVolFirstDiv(localVol, t, s); final double lv2Div = getLocalVolSecondDiv(localVol, t, s); final double lv = localVol.getVolatility(t, s); final double temp1 = (lv + s * lv1Div); final double temp2 = lv * s * (s * lv2Div + 2 * lv1Div); return forward.getDrift(t) - temp1 * temp1 - temp2; } }; // //using a log-normal distribution with a very small Standard deviation as a proxy for a // Dirac delta return new ConvectionDiffusionPDE1DStandardCoefficients( FunctionalDoublesSurface.from(a), FunctionalDoublesSurface.from(b), FunctionalDoublesSurface.from(c)); }
@Test public void testStaticConstruction() { FunctionalDoublesSurface surface = new FunctionalDoublesSurface(F1); FunctionalDoublesSurface other = FunctionalDoublesSurface.from(F1); assertEquals(surface.getFunction(), other.getFunction()); assertFalse(surface.equals(other)); surface = new FunctionalDoublesSurface(F1, NAME1); other = FunctionalDoublesSurface.from(F1, NAME1); assertEquals(surface, other); }
@Test public void ImpliedTreeEuropeanRecoveryTest() { final double interest = 0.06; final YieldAndDiscountCurve yCrv = YieldCurve.from(ConstantDoublesCurve.from(interest)); final double cost = 0.02; final double atmVol = 0.47; final ZonedDateTime date = DateUtils.getUTCDate(2010, 7, 1); final double spot = 100; final Function<Double, Double> smile = new Function<Double, Double>() { @Override public Double evaluate(final Double... tk) { ArgChecker.isTrue(tk.length == 2); final double k = tk[1]; return atmVol + (spot - k) * 0.0005; } }; final StandardOptionDataBundle data = new StandardOptionDataBundle( yCrv, cost, new VolatilitySurface(FunctionalDoublesSurface.from(smile)), spot, date); final double[] strikes = new double[] {spot * 0.9, spot, spot * 1.11}; final int nSteps = 7; final double time = 1.; for (int i = 0; i < strikes.length; ++i) { final double strike = strikes[i]; final boolean isCall = strike >= spot ? true : false; final OptionFunctionProvider1D function = new EuropeanVanillaOptionFunctionProvider(strike, time, nSteps, isCall); final double tree = _modelTrinomial.getPrice(function, data); final double black = BlackScholesFormulaRepository.price( spot, strike * 0.9, time, data.getVolatility(time, strike), interest, cost, isCall); assertEquals(tree, black, black * 0.2); } try { _model.getPrice( new EuropeanVanillaOptionFunctionProvider(strikes[2], time, nSteps, true), data); throw new RuntimeException(); } catch (Exception e) { assertTrue(e instanceof IllegalArgumentException); } }
@Test public void testEqualsAndHashCode() { FunctionalDoublesSurface other = new FunctionalDoublesSurface(F1, NAME1); assertEquals(SURFACE, other); assertEquals(SURFACE.hashCode(), other.hashCode()); other = new FunctionalDoublesSurface(F2, NAME1); assertFalse(SURFACE.equals(other)); other = new FunctionalDoublesSurface(F1, NAME2); assertFalse(SURFACE.equals(other)); other = new FunctionalDoublesSurface(F1); assertFalse(SURFACE.equals(other)); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNullPair() { SURFACE.getZValue(null); }
@Test(expectedExceptions = UnsupportedOperationException.class) public void testGetSize() { SURFACE.size(); }
static { PURE_LOG_PAY_OFF = new Function1D<Double, Double>() { final double fT = DIV_CURVES.getF(EXPIRY); final double dT = DIV_CURVES.getD(EXPIRY); @Override public Double evaluate(final Double x) { final double s = (fT - dT) * Math.exp(x) + dT; return Math.log(s); } }; final Function<Double, Double> localVol = new Function<Double, Double>() { @Override public Double evaluate(final Double... ts) { final double t = ts[0]; final double s = ts[1]; final double d = DIV_CURVES.getD(t); if (s < d) { return 0.0; } return PURE_VOL * (s - d) / s; } }; LOCAL_VOL = new LocalVolatilitySurfaceStrike(FunctionalDoublesSurface.from(localVol)); final Function<Double, Double> localVolSpecial = new Function<Double, Double>() { @Override public Double evaluate(final Double... tf) { final double t = tf[0]; final double f = tf[1]; final double rtT = DIV_CURVES.getR(t); final double dtT = DIV_CURVES.getD(t); final double ftT = DIV_CURVES.getF(t); // if (f < d) { // return 0.0; // } final double x = f / rtT / (ftT - dtT); return PURE_LOCAL_VOL.getVolatility(t, x); } }; LOCAL_VOL_SPECIAL = new LocalVolatilitySurfaceStrike(FunctionalDoublesSurface.from(localVolSpecial)); final Function<Double, Double> pureLocalVol = new Function<Double, Double>() { @Override public Double evaluate(final Double... tx) { final double t = tx[0]; final double x = tx[1]; final double f = DIV_CURVES.getF(t); final double d = DIV_CURVES.getD(t); return VOL * ((f - d) * x + d) / (f - d) / x; } }; PURE_LOCAL_VOL = new LocalVolatilitySurfaceMoneyness( FunctionalDoublesSurface.from(pureLocalVol), new ForwardCurve(1.0)); PURE_LOCAL_VOL_FLAT = new LocalVolatilitySurfaceMoneyness( ConstantDoublesSurface.from(PURE_VOL), new ForwardCurve(1.0)); LOCAL_VOL_FLAT = new LocalVolatilitySurfaceStrike(ConstantDoublesSurface.from(VOL)); }
public static ExtendedCoupledPDEDataBundle getExtendedCoupledPDEDataBundle( final ForwardCurve forward, final LocalVolatilitySurfaceStrike localVol, final double lambda1, final double lambda2, final double initialProb) { final Function<Double, Double> a = new Function<Double, Double>() { @Override public Double evaluate(final Double... ts) { ArgChecker.isTrue(ts.length == 2); return -1.0; } }; final Function<Double, Double> alpha = new Function<Double, Double>() { @Override public Double evaluate(final Double... ts) { ArgChecker.isTrue(ts.length == 2); final double t = ts[0]; final double s = ts[1]; final double temp = s * localVol.getVolatility(t, s); return 0.5 * temp * temp; } }; final Function<Double, Double> b = new Function<Double, Double>() { @Override public Double evaluate(final Double... ts) { ArgChecker.isTrue(ts.length == 2); final double t = ts[0]; final double s = ts[1]; return s * forward.getDrift(t); } }; final Function<Double, Double> beta = new Function<Double, Double>() { @Override public Double evaluate(final Double... ts) { ArgChecker.isTrue(ts.length == 2); return 1.0; } }; final Function<Double, Double> c = new Function<Double, Double>() { @Override public Double evaluate(final Double... ts) { ArgChecker.isTrue(ts.length == 2); final double t = ts[0]; return forward.getDrift(t) + lambda1; } }; // using a log-normal distribution with a very small Standard deviation as a proxy for a Dirac // delta final Function1D<Double, Double> initialCondition = new Function1D<Double, Double>() { private final double _volRootTOffset = 0.01; @Override public Double evaluate(final Double s) { if (s == 0) { return 0.0; } final double x = Math.log(s / forward.getSpot()); final NormalDistribution dist = new NormalDistribution(0, _volRootTOffset); return initialProb * dist.getPDF(x) / s; } }; return new ExtendedCoupledPDEDataBundle( FunctionalDoublesSurface.from(a), FunctionalDoublesSurface.from(b), FunctionalDoublesSurface.from(c), FunctionalDoublesSurface.from(alpha), FunctionalDoublesSurface.from(beta), lambda2, initialCondition); }
/** * Get the coefficients (a, b, c, $\alpha$ & $\beta$) for the PDE governing the evolution of the * transition density for a single underlying (i.e. 1 spatial dimension). The PDE is of the form * $frac{\partial V}{\partial t} + a(x,t)\frac{\alpha(x,t)\partial^2V}{\partial * x^2}+b(x,t)\frac{\beta(x,t)\partial V}{\partial x} +c(x,t)V=0$ where $V(x,t)$ is the density * * @param forward the forward curve * @param localVol the local volatility surface (parameterised by strike) * @return The coefficients a, b, c, $\alpha$ & $\beta$ - which are all functions of time and * asset value (space) */ public static ConvectionDiffusionPDE1DFullCoefficients getFullCoefficients( final ForwardCurve forward, final LocalVolatilitySurfaceStrike localVol) { final Function<Double, Double> a = new Function<Double, Double>() { @Override public Double evaluate(final Double... ts) { ArgChecker.isTrue(ts.length == 2); return -1.0; } }; final Function<Double, Double> alpha = new Function<Double, Double>() { @Override public Double evaluate(final Double... ts) { ArgChecker.isTrue(ts.length == 2); final double t = ts[0]; final double s = ts[1]; final double temp = s * localVol.getVolatility(t, s); return 0.5 * temp * temp; } }; final Function<Double, Double> b = new Function<Double, Double>() { @Override public Double evaluate(final Double... ts) { ArgChecker.isTrue(ts.length == 2); final double t = ts[0]; final double s = ts[1]; return s * forward.getDrift(t); } }; final Function<Double, Double> beta = new Function<Double, Double>() { @Override public Double evaluate(final Double... ts) { ArgChecker.isTrue(ts.length == 2); return 1.0; } }; final Function<Double, Double> c = new Function<Double, Double>() { @Override public Double evaluate(final Double... ts) { ArgChecker.isTrue(ts.length == 2); final double t = ts[0]; return forward.getDrift(t); } }; return new ConvectionDiffusionPDE1DFullCoefficients( FunctionalDoublesSurface.from(a), FunctionalDoublesSurface.from(b), FunctionalDoublesSurface.from(c), FunctionalDoublesSurface.from(alpha), FunctionalDoublesSurface.from(beta)); }