/** * Create a yield curve bundle with three curves. One called "Credit" with a constant rate of 5%, * one called "Discounting" with a constant rate of 4%, and one called "Forward" with a constant * rate of 4.5%. * * @return The yield curve bundle. */ public static YieldCurveBundle createCurvesBond() { final String CREDIT_CURVE_NAME = "Credit"; final String DISCOUNTING_CURVE_NAME = "Repo"; final String FORWARD_CURVE_NAME = "Forward"; final YieldAndDiscountCurve CURVE_5 = YieldCurve.from(ConstantDoublesCurve.from(0.05)); final YieldAndDiscountCurve CURVE_4 = YieldCurve.from(ConstantDoublesCurve.from(0.04)); final YieldAndDiscountCurve CURVE_45 = YieldCurve.from(ConstantDoublesCurve.from(0.045)); final YieldCurveBundle curves = new YieldCurveBundle(); curves.setCurve(CREDIT_CURVE_NAME, CURVE_5); curves.setCurve(DISCOUNTING_CURVE_NAME, CURVE_4); curves.setCurve(FORWARD_CURVE_NAME, CURVE_45); return curves; }
@Test public void test() { assertEquals(DATA.getDate(), DATE); assertEquals(DATA.getForward(), F, 0); assertEquals(DATA.getVolatilitySurface(), SURFACE); assertEquals(DATA.getInterestRateCurve(), CURVE); final double t = Math.random(); assertEquals(DATA.getDiscountFactor(t), Math.exp(-R * t), 0); BlackOptionDataBundle other = new BlackOptionDataBundle(F, CURVE, SURFACE, DATE); assertEquals(other, DATA); assertEquals(other.hashCode(), DATA.hashCode()); other = new BlackOptionDataBundle(DATA); assertEquals(other, DATA); assertEquals(other.hashCode(), DATA.hashCode()); other = new BlackOptionDataBundle(F + 1, CURVE, SURFACE, DATE); assertFalse(other.equals(DATA)); other = new BlackOptionDataBundle( F, YieldCurve.from(ConstantDoublesCurve.from(0.06)), SURFACE, DATE); assertFalse(other.equals(DATA)); other = new BlackOptionDataBundle( F, CURVE, new VolatilitySurface(ConstantDoublesSurface.from(0.6)), DATE); assertFalse(other.equals(DATA)); other = new BlackOptionDataBundle(F, CURVE, SURFACE, DATE.plusDays(1)); assertFalse(other.equals(DATA)); }
@Test public void test() { boolean isCall; double spot, strike, b, price; Expiry expiry; YieldAndDiscountCurve curve; EuropeanVanillaOptionDefinition definition; StandardOptionDataBundle initialData, data; double sigma = 0.01; for (int i = 0; i < 100; i++) { expiry = new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, RANDOM.nextDouble() * 2)); sigma += 0.03; spot = 2 * RANDOM.nextDouble() + 10; strike = 2 * RANDOM.nextDouble() + 10; curve = YieldCurve.from(ConstantDoublesCurve.from(RANDOM.nextDouble() / 10)); b = 0; // RANDOM.nextDouble() / 20; isCall = RANDOM.nextDouble() < 0.5 ? true : false; definition = new EuropeanVanillaOptionDefinition(strike, expiry, isCall); initialData = new StandardOptionDataBundle(curve, b, null, spot, DATE); data = new StandardOptionDataBundle( curve, b, new VolatilitySurface(ConstantDoublesSurface.from(sigma)), spot, DATE); price = BSM.getPricingFunction(definition).evaluate(data); assertEquals( sigma, MODEL .getSurface( Collections.<OptionDefinition, Double>singletonMap(definition, price), initialData) .getVolatility(DoublesPair.of(0., 0.)), EPS); } }
/** Test. */ @Test(groups = TestGroup.UNIT) public class ForwardStartOptionModelTest { private static final ZonedDateTime DATE = DateUtils.getUTCDate(2010, 7, 1); private static final double R = 0.08; private static final YieldCurve CURVE = YieldCurve.from(ConstantDoublesCurve.from(R)); private static final VolatilitySurface SURFACE = new VolatilitySurface(ConstantDoublesSurface.from(0.3)); private static final double B = 0.04; private static final double SPOT = 60; private static final double PERCENT = 0.1; private static final ZonedDateTime START = DateUtils.getDateOffsetWithYearFraction(DATE, 0.25); private static final ZonedDateTime EXPIRY = DateUtils.getDateOffsetWithYearFraction(DATE, 1); private static final StandardOptionDataBundle DATA = new StandardOptionDataBundle(CURVE, B, SURFACE, SPOT, DATE); private static final ForwardStartOptionDefinition FORWARD = new ForwardStartOptionDefinition( new Expiry(EXPIRY), true, new Expiry(START), PERCENT, Moneyness.OTM); private static final ForwardStartOptionDefinition NOW = new ForwardStartOptionDefinition( new Expiry(EXPIRY), true, new Expiry(DATE), PERCENT, Moneyness.OTM); private static final ForwardStartOptionDefinition END = new ForwardStartOptionDefinition( new Expiry(EXPIRY), true, new Expiry(EXPIRY), PERCENT, Moneyness.OTM); private static final EuropeanVanillaOptionDefinition VANILLA = new EuropeanVanillaOptionDefinition(SPOT * (1 + PERCENT), new Expiry(EXPIRY), true); private static final AnalyticOptionModel<ForwardStartOptionDefinition, StandardOptionDataBundle> MODEL = new ForwardStartOptionModel(); private static final AnalyticOptionModel<OptionDefinition, StandardOptionDataBundle> BSM = new BlackScholesMertonModel(); @Test(expectedExceptions = IllegalArgumentException.class) public void testNullDefinition() { MODEL.getPricingFunction(null); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullData() { MODEL.getPricingFunction(FORWARD).evaluate((StandardOptionDataBundle) null); } @Test public void test() { assertEquals(MODEL.getPricingFunction(END).evaluate(DATA), 0, 0); assertEquals( MODEL .getPricingFunction(FORWARD) .evaluate( DATA.withVolatilitySurface( new VolatilitySurface(ConstantDoublesSurface.from(1e-9)))), 0, 0); assertEquals( MODEL.getPricingFunction(NOW).evaluate(DATA), BSM.getPricingFunction(VANILLA).evaluate(DATA), 1e-4); assertEquals(MODEL.getPricingFunction(FORWARD).evaluate(DATA), 4.4064, 1e-4); } }
/** Test. */ @Test(groups = TestGroup.UNIT) public class BlackOptionDataBundleTest { private static final double R = 0.05; private static final YieldAndDiscountCurve CURVE = YieldCurve.from(ConstantDoublesCurve.from(R)); private static final VolatilitySurface SURFACE = new VolatilitySurface(ConstantDoublesSurface.from(0.35)); private static final double F = 100; private static final ZonedDateTime DATE = DateUtils.getUTCDate(2010, 5, 1); private static final BlackOptionDataBundle DATA = new BlackOptionDataBundle(F, CURVE, SURFACE, DATE); @Test public void test() { assertEquals(DATA.getDate(), DATE); assertEquals(DATA.getForward(), F, 0); assertEquals(DATA.getVolatilitySurface(), SURFACE); assertEquals(DATA.getInterestRateCurve(), CURVE); final double t = Math.random(); assertEquals(DATA.getDiscountFactor(t), Math.exp(-R * t), 0); BlackOptionDataBundle other = new BlackOptionDataBundle(F, CURVE, SURFACE, DATE); assertEquals(other, DATA); assertEquals(other.hashCode(), DATA.hashCode()); other = new BlackOptionDataBundle(DATA); assertEquals(other, DATA); assertEquals(other.hashCode(), DATA.hashCode()); other = new BlackOptionDataBundle(F + 1, CURVE, SURFACE, DATE); assertFalse(other.equals(DATA)); other = new BlackOptionDataBundle( F, YieldCurve.from(ConstantDoublesCurve.from(0.06)), SURFACE, DATE); assertFalse(other.equals(DATA)); other = new BlackOptionDataBundle( F, CURVE, new VolatilitySurface(ConstantDoublesSurface.from(0.6)), DATE); assertFalse(other.equals(DATA)); other = new BlackOptionDataBundle(F, CURVE, SURFACE, DATE.plusDays(1)); assertFalse(other.equals(DATA)); } @Test public void testBuilders() { final ZonedDateTime newDate = DATE.plusDays(1); assertEquals(DATA.withDate(newDate), new BlackOptionDataBundle(F, CURVE, SURFACE, newDate)); final double newForward = F + 1; assertEquals( DATA.withForward(newForward), new BlackOptionDataBundle(newForward, CURVE, SURFACE, DATE)); final YieldCurve newCurve = YieldCurve.from(ConstantDoublesCurve.from(0.05)); assertEquals( DATA.withInterestRateCurve(newCurve), new BlackOptionDataBundle(F, newCurve, SURFACE, DATE)); final VolatilitySurface newSurface = new VolatilitySurface(ConstantDoublesSurface.from(0.9)); assertEquals( DATA.withVolatilitySurface(newSurface), new BlackOptionDataBundle(F, CURVE, newSurface, DATE)); } }
@Test public void testBuilders() { final ZonedDateTime newDate = DATE.plusDays(1); assertEquals(DATA.withDate(newDate), new BlackOptionDataBundle(F, CURVE, SURFACE, newDate)); final double newForward = F + 1; assertEquals( DATA.withForward(newForward), new BlackOptionDataBundle(newForward, CURVE, SURFACE, DATE)); final YieldCurve newCurve = YieldCurve.from(ConstantDoublesCurve.from(0.05)); assertEquals( DATA.withInterestRateCurve(newCurve), new BlackOptionDataBundle(F, newCurve, SURFACE, DATE)); final VolatilitySurface newSurface = new VolatilitySurface(ConstantDoublesSurface.from(0.9)); assertEquals( DATA.withVolatilitySurface(newSurface), new BlackOptionDataBundle(F, CURVE, newSurface, DATE)); }
@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. */ @Test(groups = TestGroup.UNIT) public class LogOptionModelTest { private static final AnalyticOptionModel<LogOptionDefinition, StandardOptionDataBundle> MODEL = new LogOptionModel(); private static final Set<Greek> REQUIRED_GREEKS = Collections.singleton(Greek.FAIR_PRICE); private static final ZonedDateTime DATE = DateUtils.getUTCDate(2009, 1, 1); private static final Expiry EXPIRY = new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 0.75)); private static final YieldAndDiscountCurve CURVE = YieldCurve.from(ConstantDoublesCurve.from(0.08)); private static final double B = 0.04; private static final double SPOT = 100; private static final double EPS = 1e-4; @Test public void test() { LogOptionDefinition definition = getDefinition(70); assertPriceEquals(definition, 0.2, 0.3510); assertPriceEquals(definition, 0.3, 0.3422); assertPriceEquals(definition, 0.4, 0.3379); assertPriceEquals(definition, 0.5, 0.3365); assertPriceEquals(definition, 0.6, 0.3362); definition = getDefinition(130); assertPriceEquals(definition, 0.2, 0.0056); assertPriceEquals(definition, 0.3, 0.0195); assertPriceEquals(definition, 0.4, 0.0363); assertPriceEquals(definition, 0.5, 0.0532); assertPriceEquals(definition, 0.6, 0.0691); } private void assertPriceEquals( final LogOptionDefinition definition, final double sigma, final double price) { final StandardOptionDataBundle bundle = getBundle(sigma); final GreekResultCollection actual = MODEL.getGreeks(definition, bundle, REQUIRED_GREEKS); assertEquals(actual.get(Greek.FAIR_PRICE), price, EPS); } private StandardOptionDataBundle getBundle(final double sigma) { return new StandardOptionDataBundle( CURVE, B, new VolatilitySurface(ConstantDoublesSurface.from(sigma)), SPOT, DATE); } private LogOptionDefinition getDefinition(final double strike) { return new LogOptionDefinition(strike, EXPIRY); } }
public class RelativeOutperformanceOptionModelTest { private static final double S1 = 130; private static final double S2 = 100; private static final YieldAndDiscountCurve R = YieldCurve.from(ConstantDoublesCurve.from(0.07)); private static final double B1 = 0.05; private static final double B2 = 0.03; private static final VolatilitySurface SIGMA1 = new VolatilitySurface(ConstantDoublesSurface.from(0.3)); private static final VolatilitySurface SIGMA2 = new VolatilitySurface(ConstantDoublesSurface.from(0.4)); private static final ZonedDateTime DATE = DateUtils.getUTCDate(2010, 7, 1); private static final RelativeOutperformanceOptionModel MODEL = new RelativeOutperformanceOptionModel(); private static final Expiry EXPIRY = new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 0.25)); private static final double EPS = 1e-4; @Test(expectedExceptions = IllegalArgumentException.class) public void testNullDefinition() { MODEL.getPricingFunction(null); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullData() { MODEL .getPricingFunction(new RelativeOutperformanceOptionDefinition(0.1, EXPIRY, true)) .evaluate((StandardTwoAssetOptionDataBundle) null); } @Test public void test() { RelativeOutperformanceOptionDefinition option = new RelativeOutperformanceOptionDefinition(0.1, EXPIRY, true); StandardTwoAssetOptionDataBundle data = new StandardTwoAssetOptionDataBundle(R, B1, B2, SIGMA1, SIGMA2, S1, S2, -0.5, DATE); assertEquals(MODEL.getPricingFunction(option).evaluate(data), 1.2582, EPS); option = new RelativeOutperformanceOptionDefinition(0.5, EXPIRY, true); data = data.withCorrelation(0); assertEquals(MODEL.getPricingFunction(option).evaluate(data), 0.8449, EPS); option = new RelativeOutperformanceOptionDefinition(1, EXPIRY, true); data = data.withCorrelation(0.5); assertEquals(MODEL.getPricingFunction(option).evaluate(data), 0.3382, EPS); } }
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); }
public class ComplexChooserOptionModelTest { private static final YieldAndDiscountCurve CURVE = YieldCurve.from(ConstantDoublesCurve.from(0.1)); private static final double B = 0.05; private static final VolatilitySurface SURFACE = new VolatilitySurface(ConstantDoublesSurface.from(0.35)); private static final double SPOT = 50; private static final ZonedDateTime DATE = DateUtils.getUTCDate(2010, 7, 1); private static final double CHOOSE_TIME = 0.25; private static final double CALL_LIFE = 0.5; private static final double PUT_LIFE = 7. / 12; private static final Expiry CHOOSE_DATE = new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, CHOOSE_TIME)); private static final Expiry CALL_EXPIRY = new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, CALL_LIFE)); private static final Expiry PUT_EXPIRY = new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, PUT_LIFE)); private static final double CALL_STRIKE = 55; private static final double PUT_STRIKE = 48; @SuppressWarnings("unused") private static final OptionDefinition CALL = new EuropeanVanillaOptionDefinition(CALL_STRIKE, CALL_EXPIRY, true); @SuppressWarnings("unused") private static final OptionDefinition PUT = new EuropeanVanillaOptionDefinition(PUT_STRIKE, PUT_EXPIRY, false); private static final ComplexChooserOptionDefinition CHOOSER = new ComplexChooserOptionDefinition( CHOOSE_DATE, CALL_STRIKE, CALL_EXPIRY, PUT_STRIKE, PUT_EXPIRY); private static final StandardOptionDataBundle DATA = new StandardOptionDataBundle(CURVE, B, SURFACE, SPOT, DATE); private static final AnalyticOptionModel<ComplexChooserOptionDefinition, StandardOptionDataBundle> MODEL = new ComplexChooserOptionModel(); @SuppressWarnings("unused") private static final AnalyticOptionModel<OptionDefinition, StandardOptionDataBundle> BSM = new BlackScholesMertonModel(); @Test(expectedExceptions = IllegalArgumentException.class) public void testNullDefinition() { MODEL.getPricingFunction(null); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullData() { MODEL.getPricingFunction(CHOOSER).evaluate((StandardOptionDataBundle) null); } @Test public void test() { // TODO test wrt BSM // final double spot = 2; // final StandardOptionDataBundle data = DATA.withSpot(spot); // final ComplexChooserOptionDefinition chooser = new ComplexChooserOptionDefinition(new // Expiry(DATE), CALL_STRIKE, CALL_EXPIRY, PUT_STRIKE, PUT_EXPIRY); // assertEquals(MODEL.getPricingFunction(chooser).evaluate(data), BSM.getPricingFunction( // new EuropeanVanillaOptionDefinition(CALL_STRIKE, new // Expiry(DateUtil.getDateOffsetWithYearFraction(DATE, CALL_LIFE)), true)).evaluate(data), 0); assertEquals(MODEL.getPricingFunction(CHOOSER).evaluate(DATA), 6.0508, 1e-4); } }
/** Test. */ @Test public class SkewKurtosisOptionDataBundleTest { private static final double R = 0.03; private static final double SIGMA = 0.25; private static final YieldAndDiscountCurve CURVE = YieldCurve.from(ConstantDoublesCurve.from(R)); private static final double B = 0.03; private static final VolatilitySurface SURFACE = new VolatilitySurface(ConstantDoublesSurface.from(SIGMA)); private static final double SPOT = 100; private static final ZonedDateTime DATE = DateUtils.getUTCDate(2010, 5, 1); private static final double SKEW = 1.2; private static final double KURTOSIS = 4.5; private static final YieldAndDiscountCurve OTHER_CURVE = YieldCurve.from(ConstantDoublesCurve.from(R + 1)); private static final double OTHER_B = B + 1; private static final VolatilitySurface OTHER_SURFACE = new VolatilitySurface(ConstantDoublesSurface.from(SIGMA + 1)); private static final double OTHER_SPOT = SPOT + 1; private static final ZonedDateTime OTHER_DATE = DateUtils.getDateOffsetWithYearFraction(DATE, 1); private static final double OTHER_SKEW = 0.1; private static final double OTHER_KURTOSIS = 3; private static final SkewKurtosisOptionDataBundle DATA = new SkewKurtosisOptionDataBundle(CURVE, B, SURFACE, SPOT, DATE, SKEW, KURTOSIS); @Test(expectedExceptions = IllegalArgumentException.class) public void testNullBundle() { new SkewKurtosisOptionDataBundle(null); } @Test public void testGetters() { assertEquals(DATA.getInterestRateCurve(), CURVE); assertEquals(DATA.getCostOfCarry(), B, 0); assertEquals(DATA.getDate(), DATE); assertEquals(DATA.getSpot(), SPOT, 0); assertEquals(DATA.getVolatilitySurface(), SURFACE); } @Test public void testGetInterestRate() { for (int i = 0; i < 10; i++) { assertEquals(DATA.getInterestRate(Math.random()), R, 1e-15); } } @Test public void testGetVolatility() { for (int i = 0; i < 10; i++) { assertEquals(DATA.getVolatility(Math.random(), Math.random()), SIGMA, 1e-15); } } @Test public void testEqualsAndHashCode() { final SkewKurtosisOptionDataBundle data1 = new SkewKurtosisOptionDataBundle(CURVE, B, SURFACE, SPOT, DATE, SKEW, KURTOSIS); final SkewKurtosisOptionDataBundle data2 = new SkewKurtosisOptionDataBundle( new StandardOptionDataBundle(CURVE, B, SURFACE, SPOT, DATE), SKEW, KURTOSIS); final SkewKurtosisOptionDataBundle data3 = new SkewKurtosisOptionDataBundle( new SkewKurtosisOptionDataBundle(CURVE, B, SURFACE, SPOT, DATE, SKEW, KURTOSIS)); assertEquals(DATA, data1); assertEquals(DATA.hashCode(), data1.hashCode()); assertEquals(DATA, data2); assertEquals(DATA.hashCode(), data2.hashCode()); assertEquals(DATA, data3); assertEquals(DATA.hashCode(), data3.hashCode()); assertFalse( DATA.equals( new SkewKurtosisOptionDataBundle(OTHER_CURVE, B, SURFACE, SPOT, DATE, SKEW, KURTOSIS))); assertFalse( DATA.equals( new SkewKurtosisOptionDataBundle(CURVE, OTHER_B, SURFACE, SPOT, DATE, SKEW, KURTOSIS))); assertFalse( DATA.equals( new SkewKurtosisOptionDataBundle(CURVE, B, OTHER_SURFACE, SPOT, DATE, SKEW, KURTOSIS))); assertFalse( DATA.equals( new SkewKurtosisOptionDataBundle(CURVE, B, SURFACE, OTHER_SPOT, DATE, SKEW, KURTOSIS))); assertFalse( DATA.equals( new SkewKurtosisOptionDataBundle(CURVE, B, SURFACE, SPOT, OTHER_DATE, SKEW, KURTOSIS))); assertFalse( DATA.equals( new SkewKurtosisOptionDataBundle(CURVE, B, SURFACE, SPOT, DATE, OTHER_SKEW, KURTOSIS))); assertFalse( DATA.equals( new SkewKurtosisOptionDataBundle(CURVE, B, SURFACE, SPOT, DATE, SKEW, OTHER_KURTOSIS))); } @Test public void testBuilders() { assertEquals( new SkewKurtosisOptionDataBundle(OTHER_CURVE, B, SURFACE, SPOT, DATE, SKEW, KURTOSIS), DATA.withInterestRateCurve(OTHER_CURVE)); assertEquals( new SkewKurtosisOptionDataBundle(CURVE, OTHER_B, SURFACE, SPOT, DATE, SKEW, KURTOSIS), DATA.withCostOfCarry(OTHER_B)); assertEquals( new SkewKurtosisOptionDataBundle(CURVE, B, OTHER_SURFACE, SPOT, DATE, SKEW, KURTOSIS), DATA.withVolatilitySurface(OTHER_SURFACE)); assertEquals( new SkewKurtosisOptionDataBundle(CURVE, B, SURFACE, OTHER_SPOT, DATE, SKEW, KURTOSIS), DATA.withSpot(OTHER_SPOT)); assertEquals( new SkewKurtosisOptionDataBundle(CURVE, B, SURFACE, SPOT, OTHER_DATE, SKEW, KURTOSIS), DATA.withDate(OTHER_DATE)); assertEquals( new SkewKurtosisOptionDataBundle(CURVE, B, SURFACE, SPOT, DATE, OTHER_SKEW, KURTOSIS), DATA.withSkew(OTHER_SKEW)); assertEquals( new SkewKurtosisOptionDataBundle(CURVE, B, SURFACE, SPOT, DATE, SKEW, OTHER_KURTOSIS), DATA.withKurtosis(OTHER_KURTOSIS)); } }
/** Test. */ @Test(groups = TestGroup.UNIT) public class LogPayoffWithDividendsTest { private static final PDE1DCoefficientsProvider PDE_PROVIDER = new PDE1DCoefficientsProvider(); private static final InitialConditionsProvider INITIAL_COND_PROVIDER = new InitialConditionsProvider(); private static final Interpolator1D INTEPOLATOR1D = Interpolator1DFactory.DOUBLE_QUADRATIC_INSTANCE; private static final double EXPIRY = 1.5; private static final double DIVIDEND_DATE = 0.85; private static final double ALPHA = 6.0; private static final double BETA = 0.04; private static final double PURE_VOL = 0.5; private static final double VOL = 0.4; private static final double SPOT = 100.0; private static final double DRIFT = 0.1; // 0.1; private static final YieldAndDiscountCurve DISCOUNT_CURVE = new YieldCurve("yield curve", ConstantDoublesCurve.from(DRIFT)); private static final AffineDividends DIVIDENDS = new AffineDividends(new double[] {DIVIDEND_DATE}, new double[] {ALPHA}, new double[] {BETA}); private static final EquityDividendsCurvesBundle DIV_CURVES = new EquityDividendsCurvesBundle(SPOT, DISCOUNT_CURVE, DIVIDENDS); private static final LocalVolatilitySurfaceMoneyness PURE_LOCAL_VOL_FLAT; private static final LocalVolatilitySurfaceStrike LOCAL_VOL; private static final LocalVolatilitySurfaceStrike LOCAL_VOL_SPECIAL; private static final LocalVolatilitySurfaceStrike LOCAL_VOL_FLAT; private static final LocalVolatilitySurfaceMoneyness PURE_LOCAL_VOL; private static final Function1D<Double, Double> PURE_LOG_PAY_OFF; 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)); } /** * Check the the log-contract is correctly prices using a backwards PDE expressed in terms of (the * log of) the 'pure' stock price - this avoids having jumps conditions in the PDE. The pure local * volatility surface is flat. */ @Test public void backwardsLogPureSpotPDEtest() { final double fT = DIV_CURVES.getF(EXPIRY); final double lnFT = Math.log(fT); final double val = logContactPriceFromPureSpot(PURE_LOCAL_VOL_FLAT); assertEquals(PURE_VOL, Math.sqrt(-2 * (val - lnFT) / EXPIRY), 1e-6); // System.out.println(val + "\t" + Math.sqrt(-2 * (val - lnFT) / EXPIRY)); } /** * Check the the log-contract is correctly prices using a backwards PDE expressed in terms of (the * log of) the real stock price - this requires having jumps conditions in the PDE. The local * volatility surface is derived from the flat pure local volatility surface. */ @Test public void backwardsLogSpotPDEtest() { final double fT = DIV_CURVES.getF(EXPIRY); final double lnFT = Math.log(fT); final double val = logContractPriceFromSpotPDE(LOCAL_VOL); assertEquals(PURE_VOL, Math.sqrt(-2 * (val - lnFT) / EXPIRY), 1e-4); // System.out.println(val + "\t" + Math.sqrt(-2 * (val - lnFT) / EXPIRY)); } /** * Price the log-contact using the PDE in spot (with the jump conditions) with a flat local * volatility surface, and the PDE in pure spot using the pure local volatility surface derived * from the flat surface. They MUST give the same answer */ @Test public void backwardsPDETest() { final double fT = DIV_CURVES.getF(EXPIRY); final double lnFT = Math.log(fT); final double val1 = logContractPriceFromSpotPDE(LOCAL_VOL_FLAT); final double val2 = logContactPriceFromPureSpot(PURE_LOCAL_VOL); // convert to realised vol final double vol1 = Math.sqrt(-2 * (val1 - lnFT) / EXPIRY); final double vol2 = Math.sqrt(-2 * (val2 - lnFT) / EXPIRY); assertEquals(vol1, vol2, 1e-3); // System.out.println(vol1 + "\t" + vol2); } /** * 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)); } 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; } /** * 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 class PoweredOptionModelTest { private static final AnalyticOptionModel<PoweredOptionDefinition, StandardOptionDataBundle> POWERED_MODEL = new PoweredOptionModel(); private static final AnalyticOptionModel<OptionDefinition, StandardOptionDataBundle> BSM = new BlackScholesMertonModel(); private static final Set<Greek> REQUIRED_GREEKS = Collections.singleton(Greek.FAIR_PRICE); private static final ZonedDateTime DATE = DateUtils.getUTCDate(2009, 1, 1); private static final Expiry EXPIRY = new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 0.5)); private static final YieldAndDiscountCurve CURVE = new YieldCurve(ConstantDoublesCurve.from(0.1)); private static final double B = 0.07; private static final double SPOT = 100; private static final double STRIKE = 100; private static final double SMALL_EPS = 1e-9; private static final double BIG_EPS = 1e-4; @Test public void testNonIntegerPower() { try { POWERED_MODEL.getGreeks( new PoweredOptionDefinition(100, EXPIRY, 1.3, true), getBundle(0.), REQUIRED_GREEKS); Assert.fail(); } catch (final OptionPricingException e) { // Expected } } @Test public void testPowerOfOne() { PoweredOptionDefinition poweredDefinition = getPoweredDefinition(1, true); EuropeanVanillaOptionDefinition vanillaDefinition = getVanillaOption(true); assertPriceEquals(poweredDefinition, vanillaDefinition, 0.1); assertPriceEquals(poweredDefinition, vanillaDefinition, 0.2); assertPriceEquals(poweredDefinition, vanillaDefinition, 0.3); poweredDefinition = getPoweredDefinition(1, false); vanillaDefinition = getVanillaOption(false); assertPriceEquals(poweredDefinition, vanillaDefinition, 0.1); assertPriceEquals(poweredDefinition, vanillaDefinition, 0.2); assertPriceEquals(poweredDefinition, vanillaDefinition, 0.3); } @Test public void test() { PoweredOptionDefinition definition = getPoweredDefinition(2, true); assertPriceEquals(definition, 0.1, 53.4487); assertPriceEquals(definition, 0.2, 160.2944); assertPriceEquals(definition, 0.3, 339.3713); definition = getPoweredDefinition(3, true); assertPriceEquals(definition, 0.1, 758.8427); assertPriceEquals(definition, 0.2, 4608.7213); assertPriceEquals(definition, 0.3, 15624.1041); definition = getPoweredDefinition(2, false); assertPriceEquals(definition, 0.1, 9.7580); assertPriceEquals(definition, 0.2, 57.8677); assertPriceEquals(definition, 0.3, 142.2726); definition = getPoweredDefinition(3, false); assertPriceEquals(definition, 0.1, 89.6287); assertPriceEquals(definition, 0.2, 1061.2120); assertPriceEquals(definition, 0.3, 3745.1853); } private void assertPriceEquals( final PoweredOptionDefinition poweredDefinition, final EuropeanVanillaOptionDefinition vanillaDefinition, final double sigma) { final StandardOptionDataBundle bundle = getBundle(sigma); final GreekResultCollection actual = POWERED_MODEL.getGreeks(poweredDefinition, bundle, REQUIRED_GREEKS); final GreekResultCollection expected = BSM.getGreeks(vanillaDefinition, bundle, REQUIRED_GREEKS); assertEquals(expected.get(Greek.FAIR_PRICE), actual.get(Greek.FAIR_PRICE), SMALL_EPS); } private void assertPriceEquals( final PoweredOptionDefinition poweredDefinition, final double sigma, final double price) { final StandardOptionDataBundle bundle = getBundle(sigma); final GreekResultCollection actual = POWERED_MODEL.getGreeks(poweredDefinition, bundle, REQUIRED_GREEKS); assertEquals(price, actual.get(Greek.FAIR_PRICE), BIG_EPS * price); } private StandardOptionDataBundle getBundle(final double sigma) { return new StandardOptionDataBundle( CURVE, B, new VolatilitySurface(ConstantDoublesSurface.from(sigma)), SPOT, DATE); } private PoweredOptionDefinition getPoweredDefinition(final double power, final boolean isCall) { return new PoweredOptionDefinition(STRIKE, EXPIRY, power, isCall); } private EuropeanVanillaOptionDefinition getVanillaOption(final boolean isCall) { return new EuropeanVanillaOptionDefinition(STRIKE, EXPIRY, isCall); } }
@Test /** * Tests the comparison with the other implementation. This test may be removed when only one * version remains. */ public void comparison() { final AnalyticOptionModel<EuropeanStandardBarrierOptionDefinition, StandardOptionDataBundle> model = new EuropeanStandardBarrierOptionModel(); final StandardOptionDataBundle data = new StandardOptionDataBundle( YieldCurve.from(ConstantDoublesCurve.from(RATE_DOM)), COST_OF_CARRY, new VolatilitySurface(ConstantDoublesSurface.from(VOLATILITY)), SPOT, REFERENCE_DATE); final Expiry expiry = new Expiry(EXPIRY_DATE); final double priceDI1 = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_DOWN_IN, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY); final EuropeanStandardBarrierOptionDefinition optionBarrierDI = new EuropeanStandardBarrierOptionDefinition( STRIKE_MID, expiry, IS_CALL, BARRIER_DOWN_IN, REBATE); final double priceDI2 = model.getPricingFunction(optionBarrierDI).evaluate(data); assertEquals("Comparison Down In", priceDI2, priceDI1, 1.0E-10); final double priceDO1 = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_DOWN_OUT, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY); final EuropeanStandardBarrierOptionDefinition optionBarrierDO = new EuropeanStandardBarrierOptionDefinition( STRIKE_MID, expiry, IS_CALL, BARRIER_DOWN_OUT, REBATE); final double priceDO2 = model.getPricingFunction(optionBarrierDO).evaluate(data); assertEquals("Comparison Down Out", priceDO2, priceDO1, 1.0E-10); final double priceUI1 = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_UP_IN, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY); final EuropeanStandardBarrierOptionDefinition optionBarrierUI = new EuropeanStandardBarrierOptionDefinition( STRIKE_MID, expiry, IS_CALL, BARRIER_UP_IN, REBATE); final double priceUI2 = model.getPricingFunction(optionBarrierUI).evaluate(data); assertEquals("Comparison Up In", priceUI2, priceUI1, 1.0E-10); final double priceUO1 = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_UP_OUT, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY); final EuropeanStandardBarrierOptionDefinition optionBarrierUO = new EuropeanStandardBarrierOptionDefinition( STRIKE_MID, expiry, IS_CALL, BARRIER_UP_OUT, REBATE); final double priceUO2 = model.getPricingFunction(optionBarrierUO).evaluate(data); assertEquals("Comparison Up Out", priceUO2, priceUO1, 1.0E-10); final double vol0 = 0.0; final double priceVol01 = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_DOWN_IN, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, vol0); final StandardOptionDataBundle data0 = new StandardOptionDataBundle( YieldCurve.from(ConstantDoublesCurve.from(RATE_DOM)), COST_OF_CARRY, new VolatilitySurface(ConstantDoublesSurface.from(vol0)), SPOT, REFERENCE_DATE); final double priceVol02 = model.getPricingFunction(optionBarrierDI).evaluate(data0); assertEquals(priceVol02, priceVol01, 1.0E-10); }
/** Test. */ @Test(groups = TestGroup.UNIT) public class BlackScholesMertonImpliedVolatilitySurfaceModelTest { private static final RandomEngine RANDOM = new MersenneTwister64(MersenneTwister.DEFAULT_SEED); private static final BlackScholesMertonImpliedVolatilitySurfaceModel MODEL = new BlackScholesMertonImpliedVolatilitySurfaceModel(); private static final AnalyticOptionModel<OptionDefinition, StandardOptionDataBundle> BSM = new BlackScholesMertonModel(); private static final StandardOptionDataBundle DATA = new StandardOptionDataBundle( YieldCurve.from(ConstantDoublesCurve.from(0.01)), 0.1, new VolatilitySurface(ConstantDoublesSurface.from(0.01)), 100., DateUtils.getUTCDate(2010, 1, 1)); private static final ZonedDateTime DATE = DateUtils.getUTCDate(2009, 1, 1); private static final double EPS = 1e-3; @Test(expectedExceptions = IllegalArgumentException.class) public void testNullPrices() { MODEL.getSurface(null, DATA); } @Test(expectedExceptions = IllegalArgumentException.class) public void testEmptyPrices() { MODEL.getSurface(Collections.<OptionDefinition, Double>emptyMap(), DATA); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullData() { MODEL.getSurface( Collections.<OptionDefinition, Double>singletonMap( new EuropeanVanillaOptionDefinition(RANDOM.nextDouble(), new Expiry(DATE), true), 2.3), null); } @Test public void test() { boolean isCall; double spot, strike, b, price; Expiry expiry; YieldAndDiscountCurve curve; EuropeanVanillaOptionDefinition definition; StandardOptionDataBundle initialData, data; double sigma = 0.01; for (int i = 0; i < 100; i++) { expiry = new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, RANDOM.nextDouble() * 2)); sigma += 0.03; spot = 2 * RANDOM.nextDouble() + 10; strike = 2 * RANDOM.nextDouble() + 10; curve = YieldCurve.from(ConstantDoublesCurve.from(RANDOM.nextDouble() / 10)); b = 0; // RANDOM.nextDouble() / 20; isCall = RANDOM.nextDouble() < 0.5 ? true : false; definition = new EuropeanVanillaOptionDefinition(strike, expiry, isCall); initialData = new StandardOptionDataBundle(curve, b, null, spot, DATE); data = new StandardOptionDataBundle( curve, b, new VolatilitySurface(ConstantDoublesSurface.from(sigma)), spot, DATE); price = BSM.getPricingFunction(definition).evaluate(data); assertEquals( sigma, MODEL .getSurface( Collections.<OptionDefinition, Double>singletonMap(definition, price), initialData) .getVolatility(DoublesPair.of(0., 0.)), EPS); } } }
/** Test. */ @Test public class FFTOptionModelTest { private static final HashSet<Greek> GREEKS = Sets.newHashSet(Greek.FAIR_PRICE); private static final double R = 0.005; private static final YieldCurve YIELD_CURVE = YieldCurve.from(ConstantDoublesCurve.from(R)); private static final double BLACK_VOL = 0.34; private static final VolatilitySurface VOLATILITY_SURFACE = new VolatilitySurface(ConstantDoublesSurface.from(BLACK_VOL)); private static final double FORWARD = 100; private static final double T = 2; private static final ZonedDateTime DATE = DateUtils.getUTCDate(2011, 1, 1); private static final ZonedDateTime MATURITY = DATE.plusYears((int) T); private static final Expiry EXPIRY = new Expiry(MATURITY); private static final EuropeanVanillaOptionDefinition ITM_CALL = new EuropeanVanillaOptionDefinition(99, EXPIRY, true); private static final EuropeanVanillaOptionDefinition OTM_CALL = new EuropeanVanillaOptionDefinition(101, EXPIRY, true); private static final EuropeanVanillaOptionDefinition ITM_PUT = new EuropeanVanillaOptionDefinition(101, EXPIRY, false); private static final EuropeanVanillaOptionDefinition OTM_PUT = new EuropeanVanillaOptionDefinition(99, EXPIRY, false); private static final MartingaleCharacteristicExponent GAUSSIAN = new GaussianMartingaleCharacteristicExponent(BLACK_VOL); private static final StandardOptionDataBundle BSM_DATA = new StandardOptionDataBundle( YIELD_CURVE, R, VOLATILITY_SURFACE, Math.exp(-R * T) * FORWARD, DATE); private static final BlackOptionDataBundle BLACK_DATA = new BlackOptionDataBundle(FORWARD, YIELD_CURVE, VOLATILITY_SURFACE, DATE); private static final OptionModel<EuropeanVanillaOptionDefinition, BlackOptionDataBundle> FFT_MODEL = new FFTOptionModel(GAUSSIAN); private static final OptionModel<OptionDefinition, StandardOptionDataBundle> BSM_MODEL = new BlackScholesMertonModel(); private static final double EPS = 1e-2; @Test(expectedExceptions = IllegalArgumentException.class) public void testNullCharacteristicExponent1() { new FFTOptionModel(null); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullCharacteristicExponent2() { new FFTOptionModel(null, 100, 10, -0.5, 1e-8); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNegativeNStrikes() { new FFTOptionModel(GAUSSIAN, -100, 10, -0.5, 1e-8); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNegativeDelta() { new FFTOptionModel(GAUSSIAN, 100, -10, -0.5, 1e-8); } @Test(expectedExceptions = IllegalArgumentException.class) public void testZeroAlpha() { new FFTOptionModel(GAUSSIAN, 100, 10, 0, 1e-8); } @Test(expectedExceptions = IllegalArgumentException.class) public void testAlpha() { new FFTOptionModel(GAUSSIAN, 100, 10, -1, 1e-8); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNegativeTolerance() { new FFTOptionModel(GAUSSIAN, 100, 10, -1, 1e-8); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullDefinition() { FFT_MODEL.getGreeks(null, BLACK_DATA, GREEKS); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullData() { FFT_MODEL.getGreeks(ITM_CALL, null, GREEKS); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullGreeks() { FFT_MODEL.getGreeks(ITM_CALL, BLACK_DATA, null); } @Test(expectedExceptions = UnsupportedOperationException.class) public void testWrongGreeks() { FFT_MODEL.getGreeks(ITM_CALL, BLACK_DATA, Sets.newHashSet(Greek.DELTA, Greek.GAMMA)); } @Test public void testPricing() { GreekResultCollection fftPrice = FFT_MODEL.getGreeks(ITM_CALL, BLACK_DATA, GREEKS); GreekResultCollection bsmPrice = BSM_MODEL.getGreeks(ITM_CALL, BSM_DATA, GREEKS); assertEquals(fftPrice.size(), 1); assertEquals(fftPrice.get(Greek.FAIR_PRICE), bsmPrice.get(Greek.FAIR_PRICE), EPS); fftPrice = FFT_MODEL.getGreeks(OTM_CALL, BLACK_DATA, GREEKS); bsmPrice = BSM_MODEL.getGreeks(OTM_CALL, BSM_DATA, GREEKS); assertEquals(fftPrice.size(), 1); assertEquals(fftPrice.get(Greek.FAIR_PRICE), bsmPrice.get(Greek.FAIR_PRICE), EPS); fftPrice = FFT_MODEL.getGreeks(OTM_PUT, BLACK_DATA, GREEKS); bsmPrice = BSM_MODEL.getGreeks(OTM_PUT, BSM_DATA, GREEKS); assertEquals(fftPrice.size(), 1); assertEquals(fftPrice.get(Greek.FAIR_PRICE), bsmPrice.get(Greek.FAIR_PRICE), EPS); fftPrice = FFT_MODEL.getGreeks(ITM_PUT, BLACK_DATA, GREEKS); bsmPrice = BSM_MODEL.getGreeks(ITM_PUT, BSM_DATA, GREEKS); assertEquals(fftPrice.size(), 1); assertEquals(fftPrice.get(Greek.FAIR_PRICE), bsmPrice.get(Greek.FAIR_PRICE), EPS); } }
/** Test. */ @Test(groups = TestGroup.UNIT) public class ExtremeSpreadOptionDefinitionTest { private static final ZonedDateTime DATE = DateUtils.getUTCDate(2010, 7, 1); private static final Expiry EXPIRY = new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 1)); private static final Expiry PERIOD_END = new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 0.275)); private static final ExtremeSpreadOptionDefinition PUT = new ExtremeSpreadOptionDefinition(EXPIRY, false, PERIOD_END, false); private static final ExtremeSpreadOptionDefinition PUT_REVERSE = new ExtremeSpreadOptionDefinition(EXPIRY, false, PERIOD_END, true); private static final ExtremeSpreadOptionDefinition CALL = new ExtremeSpreadOptionDefinition(EXPIRY, true, PERIOD_END, false); private static final ExtremeSpreadOptionDefinition CALL_REVERSE = new ExtremeSpreadOptionDefinition(EXPIRY, true, PERIOD_END, true); private static final DoubleTimeSeries<?> SPOT_SERIES = ImmutableZonedDateTimeDoubleTimeSeries.ofUTC( new ZonedDateTime[] { DateUtils.getUTCDate(2010, 7, 1), DateUtils.getUTCDate(2010, 8, 1), DateUtils.getUTCDate(2010, 9, 1), DateUtils.getUTCDate(2010, 10, 1), DateUtils.getUTCDate(2010, 11, 1), DateUtils.getUTCDate(2010, 12, 1), DateUtils.getUTCDate(2011, 1, 1), DateUtils.getUTCDate(2011, 2, 1), DateUtils.getUTCDate(2011, 3, 1), DateUtils.getUTCDate(2011, 4, 1), DateUtils.getUTCDate(2011, 5, 1), DateUtils.getUTCDate(2011, 6, 1) }, new double[] {1, 2, 0, 1, 4, 15, 4, 4, 0, 4, 4, 4}); private static final StandardOptionWithSpotTimeSeriesDataBundle DATA = new StandardOptionWithSpotTimeSeriesDataBundle( YieldCurve.from(ConstantDoublesCurve.from(0.)), 0, new VolatilitySurface(ConstantDoublesSurface.from(0)), 2, DATE, SPOT_SERIES); @Test(expectedExceptions = IllegalArgumentException.class) public void testNullPeriodEnd() { new ExtremeSpreadOptionDefinition(EXPIRY, true, null, true); } @Test(expectedExceptions = IllegalArgumentException.class) public void testPeriodEndAfterExpiry() { new ExtremeSpreadOptionDefinition( EXPIRY, false, new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 2)), false); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullDate() { PUT.getTimeFromPeriodEnd(null); } @Test public void test() { assertEquals(PUT.getPeriodEnd(), PERIOD_END); assertFalse(PUT.isReverse()); ExtremeSpreadOptionDefinition other = new ExtremeSpreadOptionDefinition(EXPIRY, false, PERIOD_END, false); assertEquals(other, PUT); assertEquals(other.hashCode(), PUT.hashCode()); other = new ExtremeSpreadOptionDefinition( EXPIRY, false, new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 0.15)), false); assertFalse(other.equals(PUT)); other = new ExtremeSpreadOptionDefinition(EXPIRY, false, PERIOD_END, true); assertFalse(other.equals(PUT)); assertEquals(PUT.getTimeFromPeriodEnd(EXPIRY.getExpiry()), 0.725, 0); assertEquals(PUT.getTimeFromPeriodEnd(DATE), -0.275, 0); } @Test public void testExercise() { assertFalse(PUT.getExerciseFunction().shouldExercise(DATA, null)); assertFalse(PUT_REVERSE.getExerciseFunction().shouldExercise(DATA, null)); } @Test public void testPayoff() { assertEquals(CALL.getPayoffFunction().getPayoff(DATA, null), 13, 0); assertEquals(CALL_REVERSE.getPayoffFunction().getPayoff(DATA, null), 0, 0); assertEquals(PUT.getPayoffFunction().getPayoff(DATA, null), 0, 0); assertEquals(PUT_REVERSE.getPayoffFunction().getPayoff(DATA, null), 13, 0); } }
/** Test. */ @Test public class BatesGeneralizedJumpDiffusionModelTest { private static final AnalyticOptionModel< OptionDefinition, BatesGeneralizedJumpDiffusionModelDataBundle> MODEL = new BatesGeneralizedJumpDiffusionModel(); private static final AnalyticOptionModel<OptionDefinition, StandardOptionDataBundle> BSM = new BlackScholesMertonModel(); private static final YieldAndDiscountCurve CURVE = YieldCurve.from(ConstantDoublesCurve.from(0.08)); private static final double B = 0.08; private static final VolatilitySurface SURFACE = new VolatilitySurface(ConstantDoublesSurface.from(0.25)); private static final double SPOT = 100; private static final ZonedDateTime DATE = DateUtils.getUTCDate(2009, 1, 1); private static final Expiry EXPIRY1 = new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 0.1)); private static final Expiry EXPIRY2 = new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 0.25)); private static final Expiry EXPIRY3 = new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 0.5)); private static final double EPS1 = 1e-2; private static final double EPS2 = 1e-9; @Test(expectedExceptions = IllegalArgumentException.class) public void testNullDefinition() { MODEL.getPricingFunction(null); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNullData() { MODEL .getPricingFunction(new EuropeanVanillaOptionDefinition(100, EXPIRY1, true)) .evaluate((BatesGeneralizedJumpDiffusionModelDataBundle) null); } @Test public void test() { OptionDefinition call = new EuropeanVanillaOptionDefinition(80, EXPIRY1, true); BatesGeneralizedJumpDiffusionModelDataBundle data = new BatesGeneralizedJumpDiffusionModelDataBundle( CURVE, B, SURFACE, SPOT, DATE, 0., -0.04, 0.); assertEquals( BSM.getPricingFunction(call).evaluate(data), MODEL.getPricingFunction(call).evaluate(data), EPS2); call = new EuropeanVanillaOptionDefinition(80, EXPIRY1, true); data = data.withLambda(1.).withDelta(0.1); assertEquals(20.67, MODEL.getPricingFunction(call).evaluate(data), EPS1); call = new EuropeanVanillaOptionDefinition(90, EXPIRY2, true); data = data.withLambda(5.); assertEquals(14.13, MODEL.getPricingFunction(call).evaluate(data), EPS1); call = new EuropeanVanillaOptionDefinition(100, EXPIRY3, true); data = data.withLambda(10.); assertEquals(13.62, MODEL.getPricingFunction(call).evaluate(data), EPS1); data = data.withDelta(0.25); data = data.withLambda(1.); call = new EuropeanVanillaOptionDefinition(90, EXPIRY1, true); assertEquals(11.57, MODEL.getPricingFunction(call).evaluate(data), EPS1); call = new EuropeanVanillaOptionDefinition(100, EXPIRY2, true); data = data.withLambda(5.); assertEquals(12.25, MODEL.getPricingFunction(call).evaluate(data), EPS1); call = new EuropeanVanillaOptionDefinition(110, EXPIRY3, true); data = data.withLambda(10.); assertEquals(20.43, MODEL.getPricingFunction(call).evaluate(data), EPS1); data = data.withDelta(0.5); data = data.withLambda(1.); call = new EuropeanVanillaOptionDefinition(100, EXPIRY1, true); assertEquals(5.18, MODEL.getPricingFunction(call).evaluate(data), EPS1); call = new EuropeanVanillaOptionDefinition(110, EXPIRY2, true); data = data.withLambda(5.); assertEquals(16.52, MODEL.getPricingFunction(call).evaluate(data), EPS1); call = new EuropeanVanillaOptionDefinition(120, EXPIRY3, true); data = data.withLambda(10.); assertEquals(37.03, MODEL.getPricingFunction(call).evaluate(data), EPS1); } }