/** 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 ProductOptionModelTest {
  private static final double S1 = 100;
  private static final double S2 = 105;
  private static final YieldAndDiscountCurve R = new YieldCurve(ConstantDoublesCurve.from(0.07));
  private static final double B1 = 0.02;
  private static final double B2 = 0.05;
  private static final ZonedDateTime DATE = DateUtils.getUTCDate(2010, 7, 1);
  private static final ProductOptionModel MODEL = new ProductOptionModel();
  private static final Expiry EXPIRY1 =
      new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 0.1));
  private static final Expiry EXPIRY2 =
      new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 0.5));
  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 ProductOptionDefinition(0.1, EXPIRY1, true))
        .evaluate((StandardTwoAssetOptionDataBundle) null);
  }

  @Test
  public void test() {
    ProductOptionDefinition option = new ProductOptionDefinition(15000, EXPIRY1, true);
    StandardTwoAssetOptionDataBundle data =
        new StandardTwoAssetOptionDataBundle(
            R,
            B1,
            B2,
            new VolatilitySurface(ConstantDoublesSurface.from(0.2)),
            new VolatilitySurface(ConstantDoublesSurface.from(0.3)),
            S1,
            S2,
            -0.5,
            DATE);
    assertEquals(MODEL.getPricingFunction(option).evaluate(data), 0.0028, EPS);
    data =
        data.withFirstVolatilitySurface(new VolatilitySurface(ConstantDoublesSurface.from(0.3)))
            .withCorrelation(0);
    assertEquals(MODEL.getPricingFunction(option).evaluate(data), 2.4026, EPS);
    option = new ProductOptionDefinition(15000, EXPIRY2, true);
    assertEquals(MODEL.getPricingFunction(option).evaluate(data), 266.1594, EPS);
  }
}
 @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
 public void testEqualsAndHashCode() {
   final OptionDefinition call1 = new FloatingStrikeLookbackOptionDefinition(EXPIRY, true);
   final OptionDefinition put1 = new FloatingStrikeLookbackOptionDefinition(EXPIRY, false);
   final OptionDefinition call2 =
       new FloatingStrikeLookbackOptionDefinition(
           new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 3)), true);
   final OptionDefinition put2 =
       new FloatingStrikeLookbackOptionDefinition(
           new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 3)), false);
   assertFalse(CALL.equals(PUT));
   assertEquals(call1, CALL);
   assertEquals(put1, PUT);
   assertEquals(call1.hashCode(), CALL.hashCode());
   assertEquals(put1.hashCode(), PUT.hashCode());
   assertFalse(call2.equals(CALL));
   assertFalse(put2.equals(PUT));
 }
 @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. */
@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);
  }
}
 @Test
 public void testEqualsAndHashCode() {
   FXVannaVolgaVolatilityCurveDataBundle other =
       new FXVannaVolgaVolatilityCurveDataBundle(DELTA, RR, ATM, VWB, DATE);
   assertEquals(DATA, other);
   assertEquals(DATA.hashCode(), other.hashCode());
   other = new FXVannaVolgaVolatilityCurveDataBundle(-DELTA, RR, ATM, VWB, DATE);
   assertFalse(other.equals(DATA));
   other = new FXVannaVolgaVolatilityCurveDataBundle(DELTA, -RR, ATM, VWB, DATE);
   assertFalse(other.equals(DATA));
   other = new FXVannaVolgaVolatilityCurveDataBundle(DELTA, RR, ATM + 1, VWB, DATE);
   assertFalse(other.equals(DATA));
   other = new FXVannaVolgaVolatilityCurveDataBundle(DELTA, RR, ATM, -VWB, DATE);
   assertFalse(other.equals(DATA));
   other =
       new FXVannaVolgaVolatilityCurveDataBundle(
           DELTA, RR, ATM, VWB, DateUtils.getDateOffsetWithYearFraction(DATE, 1));
   assertFalse(other.equals(DATA));
 }
public class AsymmetricPowerOptionModelTest {
  private static final double B = 0.02;
  private static final double SPOT = 10;
  private static final double STRIKE = 100;
  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.08));
  private static final VolatilitySurface SURFACE =
      new VolatilitySurface(ConstantDoublesSurface.from(0.1));
  private static final StandardOptionDataBundle BUNDLE =
      new StandardOptionDataBundle(CURVE, B, SURFACE, SPOT, DATE);
  private static final AnalyticOptionModel<
          AsymmetricPowerOptionDefinition, StandardOptionDataBundle>
      MODEL = new AsymmetricPowerOptionModel();
  private static final AnalyticOptionModel<OptionDefinition, StandardOptionDataBundle> BS_MODEL =
      new BlackScholesMertonModel();
  private static final Set<Greek> REQUIRED_GREEKS = Collections.singleton(Greek.FAIR_PRICE);
  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 AsymmetricPowerOptionDefinition(STRIKE, EXPIRY, 1, true))
        .evaluate((StandardOptionDataBundle) null);
  }

  @Test
  public void test() {
    assertEquals(getPrice(1.9, true), 0.3102, EPS);
    assertEquals(getPrice(1.95, true), 1.9320, EPS);
    assertEquals(getPrice(2., true), 6.7862, EPS);
    assertEquals(getPrice(2.05, true), 15.8587, EPS);
    assertEquals(getPrice(2.1, true), 28.4341, EPS);
    assertEquals(getPrice(1.9, false), 18.2738, EPS);
    assertEquals(getPrice(1.95, false), 10.2890, EPS);
    assertEquals(getPrice(2., false), 4.3539, EPS);
    assertEquals(getPrice(2.05, false), 1.3089, EPS);
    assertEquals(getPrice(2.1, false), 0.2745, EPS);

    for (int i = 0; i < 5; i++) {
      final double power = 1.9 + 0.05 * i;
      assertEquals(getPrice(power, true), getBSPrice(power, true), EPS);
      assertEquals(getPrice(power, false), getBSPrice(power, false), EPS);
    }
  }

  private double getPrice(final double power, final boolean isCall) {
    return MODEL
        .getGreeks(getDefinition(power, isCall), BUNDLE, REQUIRED_GREEKS)
        .get(Greek.FAIR_PRICE);
  }

  private double getBSPrice(final double power, final boolean isCall) {
    final StandardOptionDataBundle bsBundle = getModifiedDataBundle(BUNDLE, power);
    return BS_MODEL
        .getGreeks(getDefinition(power, isCall), bsBundle, REQUIRED_GREEKS)
        .get(Greek.FAIR_PRICE);
  }

  private AsymmetricPowerOptionDefinition getDefinition(final double power, final boolean isCall) {
    return new AsymmetricPowerOptionDefinition(STRIKE, EXPIRY, power, isCall);
  }

  private StandardOptionDataBundle getModifiedDataBundle(
      final StandardOptionDataBundle data, final double p) {
    final double t = DateUtils.getDifferenceInYears(DATE, EXPIRY);
    final double spot = Math.pow(data.getSpot(), p);
    double sigma = data.getVolatility(t, STRIKE);
    final double b = p * (data.getCostOfCarry() + (p - 1) * sigma * sigma * 0.5);
    sigma *= p;
    return new StandardOptionDataBundle(
        CURVE, b, new VolatilitySurface(ConstantDoublesSurface.from(sigma)), spot, DATE);
  }
}
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(expectedExceptions = IllegalArgumentException.class)
 public void testPeriodEndAfterExpiry() {
   new ExtremeSpreadOptionDefinition(
       EXPIRY, false, new Expiry(DateUtils.getDateOffsetWithYearFraction(DATE, 2)), false);
 }
/** 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);
  }
}
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);
  }
}