@Override public double priceDelta(double expiry, PutCall putCall, double strike, double forward, double volatility) { return BlackFormulaRepository.delta(forward, strike, expiry, volatility, putCall.isCall()); }
@Override public double priceTheta(double expiry, PutCall putCall, double strike, double forward, double volatility) { return BlackFormulaRepository.driftlessTheta(forward, strike, expiry, volatility); }
@Override public double priceGamma(double expiry, double tenor, PutCall putCall, double strike, double forward, double volatility) { return BlackFormulaRepository.gamma(forward, strike, expiry, volatility); }
public void strikeForDeltaRecoveryTest() { int nStrikes = STRIKES_INPUT.length; int nVols = VOLS.length; for (int i = 0; i < nStrikes; ++i) { for (int j = 0; j < nVols; ++j) { double strike = STRIKES_INPUT[i]; double vol = VOLS[j]; double resC1 = BlackFormulaRepository.delta(FORWARD, strike, TIME_TO_EXPIRY, vol, true); double resP1 = BlackFormulaRepository.delta(FORWARD, strike, TIME_TO_EXPIRY, vol, false); double strRecovC1 = BlackFormulaRepository.strikeForDelta(FORWARD, resC1, TIME_TO_EXPIRY, vol, true); double strRecovP1 = BlackFormulaRepository.strikeForDelta(FORWARD, resP1, TIME_TO_EXPIRY, vol, false); assertEquals(strike, strRecovC1, strike * EPS); assertEquals(strike, strRecovP1, strike * EPS); } } }
public void volRecoveryTest() { int nStrikes = STRIKES_INPUT.length; int nVols = VOLS.length; for (int i = 0; i < nStrikes; ++i) { for (int j = 0; j < nVols; ++j) { double strike = STRIKES_INPUT[i]; double vol = VOLS[j]; double cPrice = BlackFormulaRepository.price(FORWARD, strike, TIME_TO_EXPIRY, vol, true); double pPrice = BlackFormulaRepository.price(FORWARD, strike, TIME_TO_EXPIRY, vol, false); double cRes = BlackFormulaRepository.impliedVolatility(cPrice, FORWARD, strike, TIME_TO_EXPIRY, true); double pRes = BlackFormulaRepository.impliedVolatility(pPrice, FORWARD, strike, TIME_TO_EXPIRY, false); assertEquals(vol, cRes, Math.abs(vol) * 1.e-8); assertEquals(vol, pRes, Math.abs(vol) * 1.e-8); } } }
public void strikeRecoveryTest() { int nStrikes = STRIKES_INPUT.length; int nVols = VOLS.length; for (int i = 0; i < nStrikes; ++i) { for (int j = 0; j < nVols; ++j) { double strike = STRIKES_INPUT[i]; double vol = VOLS[j]; double cDelta = BlackFormulaRepository.delta(FORWARD, strike, TIME_TO_EXPIRY, vol, true); double pdelta = BlackFormulaRepository.delta(FORWARD, strike, TIME_TO_EXPIRY, vol, false); double cRes = BlackFormulaRepository.impliedStrike(cDelta, true, FORWARD, TIME_TO_EXPIRY, vol); double pRes = BlackFormulaRepository.impliedStrike(pdelta, false, FORWARD, TIME_TO_EXPIRY, vol); assertEquals(strike, cRes, Math.abs(strike) * 1.e-8); assertEquals(strike, pRes, Math.abs(strike) * 1.e-8); } } }
public void test_price_formula() { double sampleVol = 0.2; for (int i = 0; i < NB_TEST; i++) { double expiryTime = VOLS.relativeTime(TEST_OPTION_EXPIRY[i]); for (int j = 0; j < NB_TEST; j++) { for (PutCall putCall : new PutCall[] {PutCall.CALL, PutCall.PUT}) { double price = VOLS.price(expiryTime, putCall, TEST_STRIKE[j], TEST_FORWARD, sampleVol); double delta = VOLS.priceDelta(expiryTime, putCall, TEST_STRIKE[j], TEST_FORWARD, sampleVol); double gamma = VOLS.priceGamma(expiryTime, putCall, TEST_STRIKE[j], TEST_FORWARD, sampleVol); double theta = VOLS.priceTheta(expiryTime, putCall, TEST_STRIKE[j], TEST_FORWARD, sampleVol); double vega = VOLS.priceVega(expiryTime, putCall, TEST_STRIKE[j], TEST_FORWARD, sampleVol); assertEquals(price, BlackFormulaRepository.price(TEST_FORWARD, TEST_STRIKE[j], expiryTime, sampleVol, putCall.isCall())); assertEquals(delta, BlackFormulaRepository.delta(TEST_FORWARD, TEST_STRIKE[j], expiryTime, sampleVol, putCall.isCall())); assertEquals(gamma, BlackFormulaRepository.gamma(TEST_FORWARD, TEST_STRIKE[j], expiryTime, sampleVol)); assertEquals(theta, BlackFormulaRepository.driftlessTheta(TEST_FORWARD, TEST_STRIKE[j], expiryTime, sampleVol)); assertEquals(vega, BlackFormulaRepository.vega(TEST_FORWARD, TEST_STRIKE[j], expiryTime, sampleVol)); } } } }
@Override public Double apply(Double x) { return price(forward, strike, timeToExpiry, x, isCall); } };
public void implied_volatility_adjoint() { double vol = 0.4342; // Deliberately picked an arbitrary vol double t = 0.1; double f = 10.0d; double shiftFd = 1.0E-6; double toleranceVol = 1.0E-3; double toleranceVolDelta = 1.0E-3; int nbPoints = 25; for (int i = 0; i <= nbPoints; i++) { double k = 0.75 * f + i * 0.5 * f / 25; double cPrice = BlackFormulaRepository.price(f, k, t, vol, true); double pPrice = BlackFormulaRepository.price(f, k, t, vol, false); ValueDerivatives ivCallAdj = BlackFormulaRepository.impliedVolatilityAdjoint(cPrice, f, k, t, true); ValueDerivatives ivPutAdj = BlackFormulaRepository.impliedVolatilityAdjoint(pPrice, f, k, t, false); assertEquals(ivCallAdj.getValue(), vol, toleranceVol); assertEquals(ivPutAdj.getValue(), vol, toleranceVol); double ivCallP = BlackFormulaRepository.impliedVolatility(cPrice + shiftFd, f, k, t, true); double ivCallM = BlackFormulaRepository.impliedVolatility(cPrice - shiftFd, f, k, t, true); double ivCallDerivative = (ivCallP - ivCallM) / (2 * shiftFd); assertEquals(ivCallAdj.getDerivative(0), ivCallDerivative, toleranceVolDelta); assertEquals(ivPutAdj.getDerivative(0), ivCallAdj.getDerivative(0), toleranceVolDelta); // Vega and its inverse are the same for call and puts } }
@Test(expectedExceptions = IllegalArgumentException.class) public void negativeFwdErrorImpliedVolatilityTest() { BlackFormulaRepository.impliedVolatility(10., -FORWARD, STRIKES_INPUT[1], TIME_TO_EXPIRY, true); }
/** * Computes the log-normal (Black) implied volatility of an out-the-money European option starting * from an initial guess and the derivative of the volatility w.r.t. the price. * * @param otmPrice The forward price, which is the market price divided by the numeraire, * for example the zero bond p(0,T) for the T-forward measure * This MUST be an OTM price, i.e. a call price for strike >= forward and a put price otherwise. * * @param forward the forward value of the underlying * @param strike the strike * @param timeToExpiry the time to expiry * @param volGuess a guess of the implied volatility * @return log-normal (Black) implied volatility and derivative with respect to the price */ public static ValueDerivatives impliedVolatilityAdjoint( double otmPrice, double forward, double strike, double timeToExpiry, double volGuess) { double impliedVolatility = impliedVolatility(otmPrice, forward, strike, timeToExpiry, volGuess); boolean isCall = strike >= forward; ValueDerivatives price = priceAdjoint(forward, strike, timeToExpiry, impliedVolatility, isCall); double dpricedvol = price.getDerivative(3); double dvoldprice = 1.0d / dpricedvol; return ValueDerivatives.of(impliedVolatility, DoubleArray.of(dvoldprice)); }
/** * Tests the strikes in a range of strikes, volatilities and call/put. */ public void impliedStrike() { int nbStrike = STRIKES_INPUT.length; int nbVols = VOLS.length; boolean callput = false; for (int loopcall = 0; loopcall < 2; loopcall++) { callput = !callput; for (int loopstrike = 0; loopstrike < nbStrike; loopstrike++) { for (int loopVols = 0; loopVols < nbVols; loopVols++) { ValueDerivatives d = BlackFormulaRepository .priceAdjoint(FORWARD, STRIKES_INPUT[loopstrike], TIME_TO_EXPIRY, VOLS[loopVols], callput); double delta = d.getDerivative(0); double strikeOutput = BlackFormulaRepository.impliedStrike(delta, callput, FORWARD, TIME_TO_EXPIRY, VOLS[loopVols]); assertEquals(STRIKES_INPUT[loopstrike], strikeOutput, 1.0E-8, "Implied strike: (data " + loopstrike + " / " + callput + ")"); } } } }
public void priceAdjoint() { double price = BlackFormulaRepository.price(F, F - DELTA_F, T, SIGMA, true); ValueDerivatives priceAdjoint = BlackFormulaRepository.priceAdjoint(F, F - DELTA_F, T, SIGMA, true); assertEquals(price, priceAdjoint.getValue(), TOLERANCE_PRICE2); double price0 = BlackFormulaRepository.price(F, F - DELTA_F, T, 0.0d, true); ValueDerivatives price0Adjoint = BlackFormulaRepository.priceAdjoint(F, F - DELTA_F, T, 0.0d, true); assertEquals(price0, price0Adjoint.getValue(), TOLERANCE_PRICE2); double priceFP = BlackFormulaRepository.price(F + deltaF, F - DELTA_F, T, SIGMA, true); double priceFM = BlackFormulaRepository.price(F - deltaF, F - DELTA_F, T, SIGMA, true); double derivativeF_FD = (priceFP - priceFM) / (2 * deltaF); assertEquals(derivativeF_FD, priceAdjoint.getDerivative(0), TOLERANCE_PRICE_DELTA); double priceKP = BlackFormulaRepository.price(F, F - DELTA_F + deltaK, T, SIGMA, true); double priceKM = BlackFormulaRepository.price(F, F - DELTA_F - deltaK, T, SIGMA, true); double derivativeK_FD = (priceKP - priceKM) / (2 * deltaK); assertEquals(derivativeK_FD, priceAdjoint.getDerivative(1), TOLERANCE_PRICE_DELTA); double priceTP = BlackFormulaRepository.price(F, F - DELTA_F, T + deltaT, SIGMA, true); double priceTM = BlackFormulaRepository.price(F, F - DELTA_F, T - deltaT, SIGMA, true); double derivativeT_FD = (priceTP - priceTM) / (2 * deltaT); assertEquals(derivativeT_FD, priceAdjoint.getDerivative(2), TOLERANCE_PRICE_DELTA); double priceVP = BlackFormulaRepository.price(F, F - DELTA_F, T, SIGMA + deltaV, true); double priceVM = BlackFormulaRepository.price(F, F - DELTA_F, T, SIGMA - deltaV, true); double derivativeV_FD = (priceVP - priceVM) / (2 * deltaV); assertEquals(derivativeV_FD, priceAdjoint.getDerivative(3), TOLERANCE_PRICE_DELTA);
double[] impliedVolatilityDerivatives = new double[nbStrikes]; for (int i = 0; i < nbStrikes; i++) { ValueDerivatives price = BlackFormulaRepository.priceAdjoint( forward + shiftInput, strikes.get(i) + shiftInput, timeToExpiry, blackVolatilities.get(i), true); // vega-[3] ValueDerivatives iv = BlackFormulaRepository.impliedVolatilityAdjoint( price.getValue(), forward + shiftOutput, strikes.get(i) + shiftOutput, timeToExpiry, true); impliedVolatility[i] = iv.getValue();
BlackFormulaRepository.priceAdjoint(forward, strike, time, sigma, putCall.equals(PutCall.CALL)); Pair<ValueDerivatives, double[][]> bs = BlackFormulaRepository.priceAdjoint2(forward, strike, time, sigma, putCall.equals(PutCall.CALL)); double[][] bsD2 = bs.getSecond(); assertEquals(priceAdjoint.getValue(), bs.getFirst().getValue(), TOLERANCE_1, "AD Second order: price"); .priceAdjoint(forward + deltaF, strike, time, sigma, putCall.equals(PutCall.CALL)); ValueDerivatives priceAdjointFM = BlackFormulaRepository .priceAdjoint(forward - deltaF, strike, time, sigma, putCall.equals(PutCall.CALL)); double derivativeFF_FD = (priceAdjointFP.getDerivative(0) - priceAdjointFM.getDerivative(0)) / (2 * deltaF); assertEquals(derivativeFF_FD, bs.getSecond()[0][0], double deltaV2 = (deltaV * deltaV); ValueDerivatives priceAdjointVP = BlackFormulaRepository .priceAdjoint(forward, strike, time, sigma + deltaV, putCall.equals(PutCall.CALL)); ValueDerivatives priceAdjointVM = BlackFormulaRepository .priceAdjoint(forward, strike, time, sigma - deltaV, putCall.equals(PutCall.CALL)); double derivativeVV_FD = (priceAdjointVP.getDerivative(3) - priceAdjointVM.getDerivative(3)) / (2 * deltaV); assertEquals(derivativeVV_FD, bsD2[2][2], TOLERANCE_2_VOL_VOL * Math.abs(bs.getFirst().getValue() / deltaV2), .priceAdjoint(forward, strike + deltaK, time, sigma, putCall.equals(PutCall.CALL)); ValueDerivatives priceAdjointKM = BlackFormulaRepository .priceAdjoint(forward, strike - deltaK, time, sigma, putCall.equals(PutCall.CALL)); double derivativeKK_FD = (priceAdjointKP.getDerivative(1) - priceAdjointKM.getDerivative(1)) / (2 * deltaK); assertEquals(derivativeKK_FD, bsD2[1][1], TOLERANCE_2_STR_STR * Math.abs(derivativeKK_FD),
private void priceCheck(double[] strikes) { for (int i = 0; i < N; i++) { double ivBlackComputed = BlackFormulaRepository .impliedVolatilityFromNormalApproximated(FORWARD, strikes[i], TIME_TO_EXPIRY, SIGMA_NORMAL[i]); double priceBlackComputed = BlackFormulaRepository .price(FORWARD, strikes[i], TIME_TO_EXPIRY, ivBlackComputed, true); double priceNormal = NormalFormulaRepository .price(FORWARD, strikes[i], TIME_TO_EXPIRY, SIGMA_NORMAL[i], CALL); assertEquals(priceNormal, priceBlackComputed, TOLERANCE_PRICE); } }
@Test(expectedExceptions = IllegalArgumentException.class) public void wrong_strike2() { BlackFormulaRepository.impliedVolatilityFromNormalApproximated2(FORWARD, -1.0d, TIME_TO_EXPIRY, 0.20d); }
/** * Tests the strikes computations. */ public void strike() { double[] strike = SMILE.strike(FORWARD).toArrayUnsafe(); DoubleArray volatility = SMILE.getVolatility(); int nbDelta = DELTA.size(); for (int loopdelta = 0; loopdelta < nbDelta; loopdelta++) { ValueDerivatives dPut = BlackFormulaRepository.priceAdjoint( FORWARD, strike[loopdelta], TIME_TO_EXPIRY, volatility.get(loopdelta), false); assertEquals(-DELTA.get(loopdelta), dPut.getDerivative(0), 1e-8, "Strike: Put " + loopdelta); ValueDerivatives dCall = BlackFormulaRepository.priceAdjoint( FORWARD, strike[2 * nbDelta - loopdelta], TIME_TO_EXPIRY, volatility.get(2 * nbDelta - loopdelta), true); assertEquals(DELTA.get(loopdelta), dCall.getDerivative(0), 1e-8, "Strike: Call " + loopdelta); } ValueDerivatives dPut = BlackFormulaRepository.priceAdjoint( FORWARD, strike[nbDelta], TIME_TO_EXPIRY, volatility.get(nbDelta), false); ValueDerivatives dCall = BlackFormulaRepository.priceAdjoint( FORWARD, strike[nbDelta], TIME_TO_EXPIRY, volatility.get(nbDelta), true); assertEquals(0.0, dCall.getDerivative(0) + dPut.getDerivative(0), 1e-8, "Strike: ATM"); }
public void implied_volatility_from_normal_adjoint() { double shiftFd = 1.0E-6; for (int i = 0; i < N; i++) { double ivBlackComputed = BlackFormulaRepository .impliedVolatilityFromNormalApproximated(FORWARD, STRIKES[i], TIME_TO_EXPIRY, SIGMA_NORMAL[i]); ValueDerivatives ivBlackAdj = BlackFormulaRepository .impliedVolatilityFromNormalApproximatedAdjoint(FORWARD, STRIKES[i], TIME_TO_EXPIRY, SIGMA_NORMAL[i]); assertEquals(ivBlackComputed, ivBlackAdj.getValue(), TOLERANCE_1); assertEquals(1, ivBlackAdj.getDerivatives().size()); double ivBlackComputedP = BlackFormulaRepository .impliedVolatilityFromNormalApproximated(FORWARD, STRIKES[i], TIME_TO_EXPIRY, SIGMA_NORMAL[i] + shiftFd); double ivBlackComputedM = BlackFormulaRepository .impliedVolatilityFromNormalApproximated(FORWARD, STRIKES[i], TIME_TO_EXPIRY, SIGMA_NORMAL[i] - shiftFd); double derivativeApproximated = (ivBlackComputedP - ivBlackComputedM) / (2 * shiftFd); assertEquals(derivativeApproximated, ivBlackAdj.getDerivative(0), TOLERANCE_VOL_DELTA); } }
/** * Tests the strikes in a range of strikes, volatilities and call/put. */ public void impliedStrikeDerivatives() { double[] delta = new double[] {0.25, -0.25, 0.49}; boolean[] cap = new boolean[] {true, false, true}; double[] forward = new double[] {104, 100, 10}; double[] time = new double[] {2.5, 5.0, 0.5}; double[] vol = new double[] {0.25, 0.10, 0.50}; double shift = 0.000001; double shiftF = 0.001; double[] derivatives = new double[4]; for (int loop = 0; loop < delta.length; loop++) { double strike = BlackFormulaRepository.impliedStrike(delta[loop], cap[loop], forward[loop], time[loop], vol[loop], derivatives); double strikeD = BlackFormulaRepository.impliedStrike(delta[loop] + shift, cap[loop], forward[loop], time[loop], vol[loop]); assertEquals((strikeD - strike) / shift, derivatives[0], 1.0E-3, "Implied strike: derivative delta"); double strikeF = BlackFormulaRepository.impliedStrike(delta[loop], cap[loop], forward[loop] + shiftF, time[loop], vol[loop]); assertEquals((strikeF - strike) / shiftF, derivatives[1], 1.0E-5, "Implied strike: derivative forward"); double strikeT = BlackFormulaRepository.impliedStrike(delta[loop], cap[loop], forward[loop], time[loop] + shift, vol[loop]); assertEquals((strikeT - strike) / shift, derivatives[2], 1.0E-4, "Implied strike: derivative time"); double strikeV = BlackFormulaRepository.impliedStrike(delta[loop], cap[loop], forward[loop], time[loop], vol[loop] + shift); assertEquals((strikeV - strike) / shift, derivatives[3], 1.0E-3, "Implied strike: derivative volatility"); } }