DigitalOptionFunction { public static double price(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry, final boolean isCall) { ArgumentChecker.isTrue(spot > 0.0, "negative/NaN spot; have {}", spot); ArgumentChecker.isTrue(strike > 0.0, "negative/NaN strike; have {}", strike); ArgumentChecker.isTrue(timeToExpiry > 0.0, "negative/NaN timeToExpiry; have {}", timeToExpiry); ArgumentChecker.isTrue(lognormalVol > 0.0, "negative/NaN lognormalVol; have {}", lognormalVol); ArgumentChecker.isFalse(Double.isNaN(interestRate), "interestRate is NaN"); ArgumentChecker.isFalse(Double.isNaN(costOfCarry), "costOfCarry is NaN"); final double d = (Math.log(spot / strike) + (costOfCarry - 0.5 * lognormalVol * lognormalVol) * timeToExpiry) / lognormalVol / Math.sqrt(timeToExpiry); final double sign = isCall ? 1. : -1.; return Math.exp(-interestRate * timeToExpiry) * NORMAL.getCDF(sign * d); }  static double price(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry, final boolean isCall); static double delta(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate,
      final double costOfCarry, final boolean isCall); static double gamma(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate,
      final double costOfCarry, final boolean isCall); static double theta(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate,
      final double costOfCarry, final boolean isCall); static double driftlessTheta(final double forward, final double strike, final double timeToExpiry, final double lognormalVol, final boolean isCall); static double vega(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate,
      final double costOfCarry, final boolean isCall);  }
@Test public void presentValueDomestic() { final double strike = 1.45; final boolean isCall = true; final boolean isLong = true; final double notional = 100000000; final ZonedDateTime payDate = ScheduleCalculator.getAdjustedDate(REFERENCE_DATE, Period.ofMonths(9), BUSINESS_DAY, CALENDAR); final ZonedDateTime expDate = ScheduleCalculator.getAdjustedDate(payDate, -SETTLEMENT_DAYS, CALENDAR); final double timeToExpiry = TimeCalculator.getTimeBetween(REFERENCE_DATE, expDate); final ForexDefinition forexUnderlyingDefinition = new ForexDefinition(EUR, USD, payDate, notional, strike); final ForexOptionDigitalDefinition forexOptionDefinition = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, isCall, isLong); final ForexOptionDigital forexOption = forexOptionDefinition.toDerivative(REFERENCE_DATE); final double dfDomestic = CURVES.getCurve(CURVES_NAME[1]).getDiscountFactor(forexOption.getUnderlyingForex().getPaymentTime()); final double dfForeign = CURVES.getCurve(CURVES_NAME[0]).getDiscountFactor(forexOption.getUnderlyingForex().getPaymentTime()); final double rDomestic = CURVES.getCurve(CURVES_NAME[1]).getInterestRate(forexOption.getUnderlyingForex().getPaymentTime()); final double rForeign = CURVES.getCurve(CURVES_NAME[0]).getInterestRate(forexOption.getUnderlyingForex().getPaymentTime()); final double forward = SPOT * dfForeign / dfDomestic; final double volatility = SMILE_TERM.getVolatility(new Triple<>(timeToExpiry, forward, forward)); final double sigmaRootT = volatility * Math.sqrt(forexOption.getExpirationTime()); final double dM = Math.log(forward / strike) / sigmaRootT - 0.5 * sigmaRootT; final int omega = isCall ? 1 : -1; final double pvExpected = Math.abs(forexOption.getUnderlyingForex().getPaymentCurrency1().getAmount()) * dfDomestic * NORMAL.getCDF(omega * dM) * (isLong ? 1.0 : -1.0); final double price = Math.abs(forexOption.getUnderlyingForex().getPaymentCurrency1().getAmount()) * DigitalOptionFunction.price(SPOT, strike, forexOption.getUnderlyingForex().getPaymentTime(), volatility, rDomestic, rDomestic - rForeign, isCall); assertEquals(price, pvExpected, pvExpected * 1.e-14); }
@Test(expectedExceptions = IllegalArgumentException.class) public void negativeSpotPriceTest() { DigitalOptionFunction.price(-SPOT, STRIKES[0], TIME, VOLS[1], INTERESTS[1], INTERESTS[1] - DIVIDENDS[1], true); }
@Test(expectedExceptions = IllegalArgumentException.class) public void negativeStrikePriceTest() { DigitalOptionFunction.price(SPOT, -STRIKES[0], TIME, VOLS[1], INTERESTS[1], INTERESTS[1] - DIVIDENDS[1], true); }
@Test(expectedExceptions = IllegalArgumentException.class) public void negativeTimePriceTest() { DigitalOptionFunction.price(SPOT, STRIKES[0], -TIME, VOLS[1], INTERESTS[1], INTERESTS[1] - DIVIDENDS[1], true); }
@Test(expectedExceptions = IllegalArgumentException.class) public void negativeVolPriceTest() { DigitalOptionFunction.price(SPOT, STRIKES[0], TIME, -VOLS[1], INTERESTS[1], INTERESTS[1] - DIVIDENDS[1], true); }