@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); } }
static { FORWARD_CURVE = new ForwardCurve(SPOT, DRIFT); LOCAL_VOL = new LocalVolatilitySurfaceMoneyness(ConstantDoublesSurface.from(FLAT_VOL), FORWARD_CURVE); PDE = PDE_DATA_PROVIDER.getLogBackwardsLocalVol(EXPIRY, LOCAL_VOL); INITIAL_COND = INT_COND_PROVIDER.getLogContractPayoffInLogCoordinate(); }
/** 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); } }
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); } }
/** 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 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); }
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)); } }
private StandardOptionDataBundle getBundle(final double sigma) { return new StandardOptionDataBundle( CURVE, B, new VolatilitySurface(ConstantDoublesSurface.from(sigma)), SPOT, DATE); }
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)); }
/** 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); } }
@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); }