@Test /** Tests the adjoint implementation (with computation of the derivatives). */ public void adjointPrice() { final double[] derivatives = new double[5]; final double priceDI = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_DOWN_IN, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY); final double priceDIAdjoint = BARRIER_FUNCTION.getPriceAdjoint( VANILLA_CALL_K100, BARRIER_DOWN_IN, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY, derivatives); assertEquals("Black single barrier: Adjoint price Down In", priceDI, priceDIAdjoint, 1.0E-10); final double priceDO = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_DOWN_OUT, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY); final double priceDOAdjoint = BARRIER_FUNCTION.getPriceAdjoint( VANILLA_CALL_K100, BARRIER_DOWN_OUT, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY, derivatives); assertEquals("Black single barrier: Adjoint price Down Out", priceDO, priceDOAdjoint, 1.0E-10); final double priceUI = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_UP_IN, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY); final double priceUIAdjoint = BARRIER_FUNCTION.getPriceAdjoint( VANILLA_CALL_K100, BARRIER_UP_IN, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY, derivatives); assertEquals("Black single barrier: Adjoint price Up In", priceUI, priceUIAdjoint, 1.0E-10); final double priceUO = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_UP_OUT, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY); final double priceUOAdjoint = BARRIER_FUNCTION.getPriceAdjoint( VANILLA_CALL_K100, BARRIER_UP_OUT, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY, derivatives); assertEquals("Black single barrier: Adjoint price Up Out", priceUO, priceUOAdjoint, 1.0E-10); }
@Test /** Tests the adjoint implementation (with computation of the derivatives). */ public void adjointDerivatives() { final double shiftSpot = 0.001; final double shiftRate = 1.0E-8; final double shiftCoC = 1.0E-8; final double shiftVol = 1.0E-8; final double[] derivatives = new double[5]; // DOWN-IN final double priceDI = BARRIER_FUNCTION.getPriceAdjoint( VANILLA_CALL_K100, BARRIER_DOWN_IN, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY, derivatives); final double priceDISpot = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_DOWN_IN, REBATE, SPOT + shiftSpot, COST_OF_CARRY, RATE_DOM, VOLATILITY); assertEquals( "Black single barrier: Adjoint spot derivative - Down In", (priceDISpot - priceDI) / shiftSpot, derivatives[0], 1.0E-5); final double priceDIRate = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_DOWN_IN, REBATE, SPOT, COST_OF_CARRY, RATE_DOM + shiftRate, VOLATILITY); assertEquals( "Black single barrier: Adjoint rate derivative - Down In", (priceDIRate - priceDI) / shiftRate, derivatives[2], 1.0E-5); final double priceDICoC = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_DOWN_IN, REBATE, SPOT, COST_OF_CARRY + shiftCoC, RATE_DOM, VOLATILITY); assertEquals( "Black single barrier: Adjoint cost-of-carry derivative - Down In", (priceDICoC - priceDI) / shiftCoC, derivatives[3], 1.0E-5); final double priceDIVol = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_DOWN_IN, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY + shiftVol); assertEquals( "Black single barrier: Adjoint cost-of-carry derivative - Down In", (priceDIVol - priceDI) / shiftVol, derivatives[4], 1.0E-4); // DOWN-OUT final double priceDO = BARRIER_FUNCTION.getPriceAdjoint( VANILLA_CALL_K100, BARRIER_DOWN_OUT, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY, derivatives); final double priceDOSpot = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_DOWN_OUT, REBATE, SPOT + shiftSpot, COST_OF_CARRY, RATE_DOM, VOLATILITY); assertEquals( "Black single barrier: Adjoint spot derivative - Down Out", (priceDOSpot - priceDO) / shiftSpot, derivatives[0], 2.0E-4); final double priceDORate = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_DOWN_OUT, REBATE, SPOT, COST_OF_CARRY, RATE_DOM + shiftRate, VOLATILITY); assertEquals( "Black single barrier: Adjoint rate derivative - Down Out", (priceDORate - priceDO) / shiftRate, derivatives[2], 1.0E-5); final double priceDOCoC = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_DOWN_OUT, REBATE, SPOT, COST_OF_CARRY + shiftCoC, RATE_DOM, VOLATILITY); assertEquals( "Black single barrier: Adjoint cost-of-carry derivative - Down Out", (priceDOCoC - priceDO) / shiftCoC, derivatives[3], 1.0E-4); final double priceDOVol = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_DOWN_OUT, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY + shiftVol); assertEquals( "Black single barrier: Adjoint cost-of-carry derivative - Down Out", (priceDOVol - priceDO) / shiftVol, derivatives[4], 1.0E-4); // UP-IN final double priceUI = BARRIER_FUNCTION.getPriceAdjoint( VANILLA_CALL_K100, BARRIER_UP_IN, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY, derivatives); final double priceUISpot = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_UP_IN, REBATE, SPOT + shiftSpot, COST_OF_CARRY, RATE_DOM, VOLATILITY); assertEquals( "Black single barrier: Adjoint spot derivative - Up In", (priceUISpot - priceUI) / shiftSpot, derivatives[0], 2.0E-4); final double priceUIRate = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_UP_IN, REBATE, SPOT, COST_OF_CARRY, RATE_DOM + shiftRate, VOLATILITY); assertEquals( "Black single barrier: Adjoint rate derivative - Up In", (priceUIRate - priceUI) / shiftRate, derivatives[2], 1.0E-5); final double priceUICoC = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_UP_IN, REBATE, SPOT, COST_OF_CARRY + shiftCoC, RATE_DOM, VOLATILITY); assertEquals( "Black single barrier: Adjoint cost-of-carry derivative - Up In", (priceUICoC - priceUI) / shiftCoC, derivatives[3], 1.0E-4); final double priceUIVol = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_UP_IN, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY + shiftVol); assertEquals( "Black single barrier: Adjoint cost-of-carry derivative - Up In", (priceUIVol - priceUI) / shiftVol, derivatives[4], 1.0E-5); // UP-OUT final double priceUO = BARRIER_FUNCTION.getPriceAdjoint( VANILLA_CALL_K100, BARRIER_UP_OUT, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY, derivatives); final double priceUOSpot = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_UP_OUT, REBATE, SPOT + shiftSpot, COST_OF_CARRY, RATE_DOM, VOLATILITY); assertEquals( "Black single barrier: Adjoint spot derivative - Up Out", (priceUOSpot - priceUO) / shiftSpot, derivatives[0], 1.0E-4); final double priceUORate = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_UP_OUT, REBATE, SPOT, COST_OF_CARRY, RATE_DOM + shiftRate, VOLATILITY); assertEquals( "Black single barrier: Adjoint rate derivative - Up Out", (priceUORate - priceUO) / shiftRate, derivatives[2], 1.0E-5); final double priceUOCoC = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_UP_OUT, REBATE, SPOT, COST_OF_CARRY + shiftCoC, RATE_DOM, VOLATILITY); assertEquals( "Black single barrier: Adjoint cost-of-carry derivative - Up Out", (priceUOCoC - priceUO) / shiftCoC, derivatives[3], 1.0E-5); final double priceUOVol = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, BARRIER_UP_OUT, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY + shiftVol); assertEquals( "Black single barrier: Adjoint cost-of-carry derivative - Up Out", (priceUOVol - priceUO) / shiftVol, derivatives[4], 2.0E-5); }
@Test /** * Tests the 'In-Out Parity' condition: The price of a Knock-In plus a Knock-Out of arbitrary * barrier level must equal that of the underlying vanilla option + value of the rebate */ public void impossibleToHitBarrierIsVanilla() { final Barrier veryLowKnockIn = new Barrier(KnockType.IN, BarrierType.DOWN, ObservationType.CONTINUOUS, 1e-6); final Barrier veryLowKnockOut = new Barrier(KnockType.OUT, BarrierType.DOWN, ObservationType.CONTINUOUS, 1e-6); final Barrier veryHighKnockIn = new Barrier(KnockType.IN, BarrierType.UP, ObservationType.CONTINUOUS, 1e6); final Barrier veryHighKnockOut = new Barrier(KnockType.OUT, BarrierType.UP, ObservationType.CONTINUOUS, 1e6); final double pxRebate = DF_DOM * REBATE; final Function1D<BlackFunctionData, Double> fcnVanillaCall = BLACK_FUNCTION.getPriceFunction(VANILLA_CALL_K100); final double pxVanillaCall = fcnVanillaCall.evaluate(DATA_BLACK); // KnockIn's with impossible to reach barrier's are guaranteed to pay the rebate at maturity final double pxDownInPut = BARRIER_FUNCTION.getPrice( VANILLA_PUT_K100, veryLowKnockIn, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY); assertTrue("VeryLowKnockInBarrier doesn't match rebate", pxDownInPut / pxRebate - 1 < 1e-6); final double pxDownInCall = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, veryLowKnockIn, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY); assertTrue("VeryLowKnockInBarrier doesn't match rebate", pxDownInCall / pxRebate - 1 < 1e-6); final double pxUpInCall = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, veryHighKnockIn, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY); assertTrue("VeryHighKnockInBarrier doesn't match rebate", pxUpInCall / pxRebate - 1 < 1e-6); // KnockOut's with impossible to reach barrier's are guaranteed to pay the value of the // underlying vanilla final double pxDownOutCall = BARRIER_FUNCTION.getPrice( VANILLA_CALL_K100, veryLowKnockOut, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY); assertTrue( "VeryLowKnockInBarrier doesn't match rebate", Math.abs(pxDownOutCall / pxVanillaCall - 1) < 1e-6); // Derivatives final double[] derivs = new double[5]; BARRIER_FUNCTION.getPriceAdjoint( VANILLA_CALL_K100, veryLowKnockIn, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY, derivs); assertTrue( "Impossible KnockIn: rate sens is incorrect", derivs[2] / Math.abs((-1 * EXPIRY_TIME * DF_DOM * REBATE) - 1) < 1e-6); assertEquals( "Impossible KnockIn: Encountered derivative, other than d/dr, != 0", 0.0, derivs[0] + derivs[1] + derivs[3] + derivs[4], 1.0e-6); BARRIER_FUNCTION.getPriceAdjoint( VANILLA_CALL_K100, veryHighKnockIn, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY, derivs); assertTrue( "Impossible KnockIn: rate sens is incorrect", derivs[2] / Math.abs((-1 * EXPIRY_TIME * DF_DOM * REBATE) - 1) < 1e-6); assertEquals( "Impossible KnockIn: Encountered derivative, other than d/dr, != 0", 0.0, derivs[0] + derivs[1] + derivs[3] + derivs[4], 1.0e-6); // Barrier: [0] spot, [1] strike, [2] rate, [3] cost-of-carry, [4] volatility. BARRIER_FUNCTION.getPriceAdjoint( VANILLA_CALL_K100, veryLowKnockOut, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY, derivs); // Vanilla: [0] the price, [1] the derivative with respect to the forward, [2] the derivative // with respect to the volatility and [3] the derivative with respect to the strike. final double[] vanillaDerivs = BLACK_FUNCTION.getPriceAdjoint(VANILLA_CALL_K100, DATA_BLACK); assertEquals( "Impossible KnockOut: Vega doesn't match vanilla", vanillaDerivs[2], derivs[4], 1e-6); assertEquals( "Impossible KnockOut: Dual Delta (d/dK) doesn't match vanilla", vanillaDerivs[3], derivs[1], 1e-6); assertEquals( "Impossible KnockOut: Delta doesn't match vanilla", vanillaDerivs[1] * DF_FOR / DF_DOM, derivs[0], 1e-6); BARRIER_FUNCTION.getPriceAdjoint( VANILLA_CALL_K100, veryHighKnockOut, REBATE, SPOT, COST_OF_CARRY, RATE_DOM, VOLATILITY, derivs); assertEquals( "Impossible KnockOut: Vega doesn't match vanilla", vanillaDerivs[2], derivs[4], 1e-6); assertEquals( "Impossible KnockOut: Dual Delta (d/dK) doesn't match vanilla", vanillaDerivs[3], derivs[1], 1e-6); assertEquals( "Impossible KnockOut: Delta doesn't match vanilla", vanillaDerivs[1] * DF_FOR / DF_DOM, derivs[0], 1e-6); }