BlackBarrierPriceFunction { public double getPrice(final EuropeanVanillaOption option, final Barrier barrier, final double rebate, final double spot, final double costOfCarry, final double rate, final double sigma) { ArgumentChecker.notNull(option, "option"); ArgumentChecker.notNull(barrier, "barrier"); final boolean isKnockIn = (barrier.getKnockType() == KnockType.IN); final boolean isDown = (barrier.getBarrierType() == BarrierType.DOWN); final double h = barrier.getBarrierLevel(); ArgumentChecker.isTrue(!(barrier.getBarrierType() == BarrierType.DOWN && spot < barrier.getBarrierLevel()), "The Data is not consistent with an alive barrier (DOWN and spot<barrier)."); ArgumentChecker.isTrue(!(barrier.getBarrierType() == BarrierType.UP && spot > barrier.getBarrierLevel()), "The Data is not consistent with an alive barrier (UP and spot>barrier)."); final boolean isCall = option.isCall(); final double t = option.getTimeToExpiry(); final double strike = option.getStrike(); final int phi = isCall ? 1 : -1; final double eta = isDown ? 1 : -1; final double df1 = Math.exp(t * (costOfCarry - rate)); final double df2 = Math.exp(-rate * t); if (CompareUtils.closeEquals(sigma, 0, 1e-16)) { return df1 * rebate; } final double sigmaSq = sigma * sigma; final double sigmaT = sigma * Math.sqrt(t); final double mu = (costOfCarry - 0.5 * sigmaSq) / sigmaSq; final double lambda = Math.sqrt(mu * mu + 2 * rate / sigmaSq); final double m1 = sigmaT * (1 + mu); final double x1 = Math.log(spot / strike) / sigmaT + m1; final double x2 = Math.log(spot / h) / sigmaT + m1; final double y1 = Math.log(h * h / spot / strike) / sigmaT + m1; final double y2 = Math.log(h / spot) / sigmaT + m1; final double z = Math.log(h / spot) / sigmaT + lambda * sigmaT; final double xA = getA(spot, strike, df1, df2, x1, sigmaT, phi); final double xB = getA(spot, strike, df1, df2, x2, sigmaT, phi); final double xC = getC(spot, strike, df1, df2, y1, sigmaT, h, mu, phi, eta); final double xD = getC(spot, strike, df1, df2, y2, sigmaT, h, mu, phi, eta); final double xE = isKnockIn ? getE(spot, rebate, df2, x2, y2, sigmaT, h, mu, eta) : getF(spot, rebate, z, sigmaT, h, mu, lambda, eta); if (isKnockIn) { if (isDown) { if (isCall) { return strike > h ? xC + xE : xA - xB + xD + xE; } return strike > h ? xB - xC + xD + xE : xA + xE; } if (isCall) { return strike > h ? xA + xE : xB - xC + xD + xE; } return strike > h ? xA - xB + xD + xE : xC + xE; } if (isDown) { if (isCall) { return strike > h ? xA - xC + xE : xB - xD + xE; } return strike > h ? xA - xB + xC - xD + xE : xE; } if (isCall) { return strike > h ? xE : xA - xB + xC - xD + xE; } return strike > h ? xB - xD + xE : xA - xC + xE; } private  BlackBarrierPriceFunction(); static BlackBarrierPriceFunction getInstance(); double getPrice(final EuropeanVanillaOption option, final Barrier barrier, final double rebate, final double spot, final double costOfCarry, final double rate, final double sigma); double getPriceAdjoint(final EuropeanVanillaOption option, final Barrier barrier, final double rebate, final double spot, final double costOfCarry, final double rate, final double sigma,
      final double[] derivatives);  }
@Test public void inOutParityWithoutRebate() { final Function1D<BlackFunctionData, Double> fcnVanillaCall = BLACK_FUNCTION.getPriceFunction(VANILLA_CALL_K100); final double pxVanillaCall = fcnVanillaCall.evaluate(DATA_BLACK); final double noRebate = 0.0; final double priceDownIn = BARRIER_FUNCTION.getPrice(VANILLA_CALL_K100, BARRIER_DOWN_IN, noRebate, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY); final double priceDownOut = BARRIER_FUNCTION.getPrice(VANILLA_CALL_K100, BARRIER_DOWN_OUT, noRebate, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY); assertEquals("Knock In-Out Parity fails", 1.0, pxVanillaCall / (priceDownIn + priceDownOut), 1.e-6); }
@Test public void inOutParityWithRebate() { final Function1D<BlackFunctionData, Double> fcnVanillaCall = BLACK_FUNCTION.getPriceFunction(VANILLA_CALL_K100); final BlackFunctionData zeroRatesMarket = new BlackFunctionData(SPOT, 1.0, VOLATILITY); final double pxVanillaCall = fcnVanillaCall.evaluate(zeroRatesMarket); final double priceDownInRebate = BARRIER_FUNCTION.getPrice(VANILLA_CALL_K100, BARRIER_DOWN_IN, REBATE, SPOT, 0.0, 0.0, VOLATILITY); final double priceDownOutRebate = BARRIER_FUNCTION.getPrice(VANILLA_CALL_K100, BARRIER_DOWN_OUT, REBATE, SPOT, 0.0, 0.0, VOLATILITY); assertEquals("Knock In-Out Parity fails", 1.0, (pxVanillaCall + REBATE) / (priceDownInRebate + priceDownOutRebate), 1.e-6); }
@Test public void inOutParityMorePathsWithRebate() { final BlackFunctionData zeroRatesMarket = new BlackFunctionData(SPOT, 1.0, VOLATILITY); final double rateDomestic = 0.0; final double rateForeign = 0.0; final double costOfCarry = rateDomestic - rateForeign; final double pxRebate = REBATE; final Function1D<BlackFunctionData, Double> fcnVanillaCall = BLACK_FUNCTION.getPriceFunction(VANILLA_CALL_K100); final double pxVanillaCall = fcnVanillaCall.evaluate(zeroRatesMarket); final Function1D<BlackFunctionData, Double> fcnVanillaPut = BLACK_FUNCTION.getPriceFunction(VANILLA_PUT_K100); final double pxVanillaPut = fcnVanillaPut.evaluate(zeroRatesMarket); final double pxDownInCall = BARRIER_FUNCTION.getPrice(VANILLA_CALL_K100, BARRIER_DOWN_IN, REBATE, SPOT, costOfCarry, rateDomestic, VOLATILITY); final double pxDownOutCall = BARRIER_FUNCTION.getPrice(VANILLA_CALL_K100, BARRIER_DOWN_OUT, REBATE, SPOT, costOfCarry, rateDomestic, VOLATILITY); assertEquals("Knock In-Out Parity fails", 1.0, (pxVanillaCall + pxRebate) / (pxDownInCall + pxDownOutCall), 1.e-6); final double pxDownInPut = BARRIER_FUNCTION.getPrice(VANILLA_PUT_K100, BARRIER_DOWN_IN, REBATE, SPOT, costOfCarry, rateDomestic, VOLATILITY); final double pxDownOutPut = BARRIER_FUNCTION.getPrice(VANILLA_PUT_K100, BARRIER_DOWN_OUT, REBATE, SPOT, costOfCarry, rateDomestic, VOLATILITY); assertTrue("Knock In-Out Parity fails", Math.abs((pxVanillaPut + pxRebate) / (pxDownInPut + pxDownOutPut) - 1) < 1.e-6); final double pxUpInCall = BARRIER_FUNCTION.getPrice(VANILLA_CALL_K100, BARRIER_UP_IN, REBATE, SPOT, costOfCarry, rateDomestic, VOLATILITY); final double pxUpOutCall = BARRIER_FUNCTION.getPrice(VANILLA_CALL_K100, BARRIER_UP_OUT, REBATE, SPOT, costOfCarry, rateDomestic, VOLATILITY); assertTrue("Knock In-Out Parity fails", Math.abs((pxVanillaCall + pxRebate) / (pxUpInCall + pxUpOutCall) - 1) < 1.e-6); final double pxUpInPut = BARRIER_FUNCTION.getPrice(VANILLA_PUT_K100, BARRIER_UP_IN, REBATE, SPOT, costOfCarry, rateDomestic, VOLATILITY); final double pxUpOutPut = BARRIER_FUNCTION.getPrice(VANILLA_PUT_K100, BARRIER_UP_OUT, REBATE, SPOT, costOfCarry, rateDomestic, VOLATILITY); assertTrue("Knock In-Out Parity fails", Math.abs((pxVanillaPut + pxRebate) / (pxUpInPut + pxUpOutPut) - 1) < 1.e-6); final Function1D<BlackFunctionData, Double> fcnVanillaPutHiK = BLACK_FUNCTION.getPriceFunction(VANILLA_PUT_KHI); final double pxVanillaPutHiK = fcnVanillaPutHiK.evaluate(zeroRatesMarket); final double pxUpInPutHiK = BARRIER_FUNCTION.getPrice(VANILLA_PUT_KHI, BARRIER_UP_IN, REBATE, SPOT, costOfCarry, rateDomestic, VOLATILITY); final double pxUpOutPutHiK = BARRIER_FUNCTION.getPrice(VANILLA_PUT_KHI, BARRIER_UP_OUT, REBATE, SPOT, costOfCarry, rateDomestic, VOLATILITY); assertTrue("Knock In-Out Parity fails", Math.abs((pxVanillaPutHiK + pxRebate) / (pxUpInPutHiK + pxUpOutPutHiK) - 1) < 1.e-6); }
@Test 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(expectedExceptions = IllegalArgumentException.class) public void exceptionDown() { BARRIER_FUNCTION.getPrice(VANILLA_CALL_K100, BARRIER_DOWN_IN, REBATE, 85.0, COST_OF_CARRY, RATE_DOM, VOLATILITY); }
@Test(expectedExceptions = IllegalArgumentException.class) public void exceptionUp() { final Barrier barrierUp = new Barrier(KnockType.IN, BarrierType.UP, ObservationType.CONTINUOUS, 90); BARRIER_FUNCTION.getPrice(VANILLA_CALL_K100, barrierUp, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY); }