BlackScholesFormulaRepository { @ExternalFunction public static double vega(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry) { 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"); double coef = 0.; if ((interestRate > LARGE && costOfCarry > LARGE) || (-interestRate > LARGE && -costOfCarry > LARGE) || Math.abs(costOfCarry - interestRate) < SMALL) { coef = 1.; } else { final double rate = costOfCarry - interestRate; if (rate > LARGE) { return costOfCarry > LARGE ? 0. : Double.POSITIVE_INFINITY; } if (-rate > LARGE) { return 0.; } coef = Math.exp(rate * timeToExpiry); } final double rootT = Math.sqrt(timeToExpiry); double sigmaRootT = lognormalVol * rootT; if (Double.isNaN(sigmaRootT)) { sigmaRootT = 1.; } double factor = Math.exp(costOfCarry * timeToExpiry); if (Double.isNaN(factor)) { factor = 1.; } double d1 = 0.; if (Math.abs(spot - strike) < SMALL || (spot > LARGE && strike > LARGE) || sigmaRootT > LARGE) { final double coefD1 = (Math.abs(costOfCarry) < SMALL && lognormalVol < SMALL) ? Math.signum(costOfCarry) + 0.5 * lognormalVol : (costOfCarry / lognormalVol + 0.5 * lognormalVol); final double tmp = coefD1 * rootT; d1 = Double.isNaN(tmp) ? 0. : tmp; } else { if (sigmaRootT < SMALL || spot > LARGE * strike || strike > LARGE * spot) { final double scnd = (Math.abs(costOfCarry) > LARGE && rootT < SMALL) ? Math.signum(costOfCarry) : costOfCarry * rootT; final double tmp = (Math.log(spot / strike) / rootT + scnd) / lognormalVol; d1 = Double.isNaN(tmp) ? 0. : tmp; } else { final double tmp = costOfCarry * rootT / lognormalVol; final double sig = (costOfCarry >= 0.) ? 1. : -1.; final double scnd = Double.isNaN(tmp) ? ((lognormalVol < LARGE && lognormalVol > SMALL) ? sig / lognormalVol : sig * rootT) : tmp; d1 = Math.log(spot / strike) / sigmaRootT + scnd + 0.5 * sigmaRootT; } } final double norm = NORMAL.getPDF(d1); final double res = norm < SMALL ? 0. : coef * norm * spot * rootT; return Double.isNaN(res) ? Double.POSITIVE_INFINITY : res; }  @ExternalFunction static double price(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry, final boolean isCall); @ExternalFunction 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 strikeForDelta(final double spot, final double spotDelta, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry,
      final boolean isCall); @ExternalFunction static double dualDelta(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry,
      final boolean isCall); @ExternalFunction static double gamma(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry); @ExternalFunction static double dualGamma(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry); @ExternalFunction static double crossGamma(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry); @ExternalFunction 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 charm(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry, final boolean isCall); static double dualCharm(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry,
      final boolean isCall); @ExternalFunction static double vega(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry); @ExternalFunction static double vanna(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry); @ExternalFunction static double dualVanna(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry); @ExternalFunction static double vomma(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry); @ExternalFunction static double vegaBleed(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry); @ExternalFunction static double rho(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry,
      final boolean isCall); @ExternalFunction static double carryRho(final double spot, final double strike, final double timeToExpiry, final double lognormalVol, final double interestRate, final double costOfCarry,
      final boolean isCall);  }
@Test public void exVegaTest() { final int nStrikes = STRIKES_INPUT.length; final int nVols = VOLS.length; final int nInt = INTEREST_RATES.length; final double inf = Double.POSITIVE_INFINITY; for (int i = 0; i < nStrikes; ++i) { for (int j = 0; j < nVols; ++j) { for (int l = 0; l < nInt; ++l) { final double rate = INTEREST_RATES[l]; final double strike = STRIKES_INPUT[i]; final double vol = VOLS[j]; final double resC1 = BlackScholesFormulaRepository.vega(1.e-12 * strike, strike, TIME_TO_EXPIRY, vol, rate, COST_OF_CARRY); final double refC1 = BlackScholesFormulaRepository.vega(0., strike, TIME_TO_EXPIRY, vol, rate, COST_OF_CARRY); final double resC2 = BlackScholesFormulaRepository.vega(1.e12 * strike, strike, TIME_TO_EXPIRY, vol, rate, COST_OF_CARRY); final double refC2 = BlackScholesFormulaRepository.vega(inf, strike, TIME_TO_EXPIRY, vol, rate, COST_OF_CARRY); final double[] resVec = new double[] {resC1, resC2 }; final double[] refVec = new double[] {refC1, refC2 }; for (int k = 0; k < 2; ++k) { if (refVec[k] > 1.e10) { assertTrue(resVec[k] > 1.e12); } else { if (refVec[k] < -1.e10) { assertTrue(resVec[k] < -1.e12); } else { if (refVec[k] == 0.) { assertTrue(Math.abs(resVec[k]) < 1.e-11); } else { assertEquals(refVec[k], resVec[k], Math.abs(refVec[k]) * 1.e-11); } } } } } } } for (int i = 0; i < nStrikes; ++i) { for (int j = 0; j < nVols; ++j) { for (int l = 0; l < nInt; ++l) { final double rate = INTEREST_RATES[l]; final double vol = VOLS[j]; final double resC1 = BlackScholesFormulaRepository.vega(SPOT, 1.e-12 * SPOT, TIME_TO_EXPIRY, vol, rate, COST_OF_CARRY); final double resC2 = BlackScholesFormulaRepository.vega(SPOT, 1.e12 * SPOT, TIME_TO_EXPIRY, vol, rate, COST_OF_CARRY); final double refC1 = BlackScholesFormulaRepository.vega(SPOT, 0., TIME_TO_EXPIRY, vol, rate, COST_OF_CARRY); final double refC2 = BlackScholesFormulaRepository.vega(SPOT, inf, TIME_TO_EXPIRY, vol, rate, COST_OF_CARRY); final double[] resVec = new double[] {resC1, resC2 }; final double[] refVec = new double[] {refC1, refC2 }; for (int k = 0; k < 2; ++k) { if (refVec[k] > 1.e10) { assertTrue(resVec[k] > 1.e12); } else { if (refVec[k] < -1.e10) { assertTrue(resVec[k] < -1.e12); } else { if (refVec[k] == 0.) { assertTrue(Math.abs(resVec[k]) < 1.e-12); } else { assertEquals(refVec[k], resVec[k], Math.abs(refVec[k]) * 1.e-10); } } } } } } } for (int i = 0; i < nStrikes; ++i) { for (int j = 0; j < nVols; ++j) { for (int l = 0; l < nInt; ++l) { final double rate = INTEREST_RATES[l]; final double strike = STRIKES_INPUT[i]; final double vol = VOLS[j]; final double resC1 = BlackScholesFormulaRepository.vega(SPOT, strike, 1e-24, vol, rate, COST_OF_CARRY); final double resC2 = BlackScholesFormulaRepository.vega(SPOT, strike, 1e24, vol, rate, COST_OF_CARRY); final double refC1 = BlackScholesFormulaRepository.vega(SPOT, strike, 0., vol, rate, COST_OF_CARRY); final double refC2 = BlackScholesFormulaRepository.vega(SPOT, strike, inf, vol, rate, COST_OF_CARRY); final double[] resVec = new double[] {resC1, resC2 }; final double[] refVec = new double[] {refC1, refC2 }; for (int k = 0; k < 2; ++k) { if (refVec[k] > 1.e10) { assertTrue(resVec[k] > 1.e9); } else { if (refVec[k] < -1.e10) { assertTrue(resVec[k] < -1.e9); } else { if (refVec[k] == 0.) { assertTrue(Math.abs(resVec[k]) < 1.e-10); } else { assertEquals(refVec[k], resVec[k], Math.abs(refVec[k]) * 1.e-10); } } } } } } } for (int i = 0; i < nStrikes; ++i) { for (int l = 0; l < nInt; ++l) { final double rate = INTEREST_RATES[l]; final double strike = STRIKES_INPUT[i]; final double resC1 = BlackScholesFormulaRepository.vega(SPOT, strike, TIME_TO_EXPIRY, 1.e-12, rate, COST_OF_CARRY); final double refC1 = BlackScholesFormulaRepository.vega(SPOT, strike, TIME_TO_EXPIRY, 0., rate, COST_OF_CARRY); final double resC2 = BlackScholesFormulaRepository.vega(SPOT, strike, TIME_TO_EXPIRY, 1.e12, rate, COST_OF_CARRY); final double refC2 = BlackScholesFormulaRepository.vega(SPOT, strike, TIME_TO_EXPIRY, inf, rate, COST_OF_CARRY); final double[] resVec = new double[] {resC1, resC2 }; final double[] refVec = new double[] {refC1, refC2 }; for (int k = 0; k < 2; ++k) { if (refVec[k] > 1.e10) { assertTrue(resVec[k] > 1.e12); } else { if (refVec[k] < -1.e10) { assertTrue(resVec[k] < -1.e12); } else { if (refVec[k] == 0.) { assertTrue(Math.abs(resVec[k]) < 1.e-12); } else { assertEquals(refVec[k], resVec[k], Math.abs(refVec[k]) * 1.e-12); } } } } } } for (int i = 0; i < nStrikes; ++i) { for (int j = 0; j < nVols; ++j) { final double strike = STRIKES_INPUT[i]; final double vol = VOLS[j]; final double resC1 = BlackScholesFormulaRepository.vega(SPOT, strike, TIME_TO_EXPIRY, vol, 1.e-12, COST_OF_CARRY); final double refC1 = BlackScholesFormulaRepository.vega(SPOT, strike, TIME_TO_EXPIRY, vol, 0., COST_OF_CARRY); final double resC2 = BlackScholesFormulaRepository.vega(SPOT, strike, TIME_TO_EXPIRY, vol, 1.e12, COST_OF_CARRY); final double refC2 = BlackScholesFormulaRepository.vega(SPOT, strike, TIME_TO_EXPIRY, vol, inf, COST_OF_CARRY); final double resC3 = BlackScholesFormulaRepository.vega(SPOT, strike, TIME_TO_EXPIRY, vol, -1.e12, COST_OF_CARRY); final double refC3 = BlackScholesFormulaRepository.vega(SPOT, strike, TIME_TO_EXPIRY, vol, -inf, COST_OF_CARRY); final double[] resVec = new double[] {resC1, resC2, resC3 }; final double[] refVec = new double[] {refC1, refC2, refC3 }; for (int k = 0; k < 3; ++k) { if (refVec[k] > 1.e10) { assertTrue(resVec[k] > 1.e12); } else { if (refVec[k] < -1.e10) { assertTrue(resVec[k] < -1.e12); } else { if (refVec[k] == 0.) { assertTrue(Math.abs(resVec[k]) < 1.e-11); } else { assertEquals(refVec[k], resVec[k], Math.abs(refVec[k]) * 1.e-11); } } } } } } for (int i = 0; i < nStrikes; ++i) { for (int j = 0; j < nVols; ++j) { for (int l = 0; l < nInt; ++l) { final double rate = INTEREST_RATES[l]; final double strike = STRIKES_INPUT[i]; final double vol = VOLS[j]; final double resC1 = BlackScholesFormulaRepository.vega(SPOT, strike, TIME_TO_EXPIRY, vol, rate, 1.e-12); final double refC1 = BlackScholesFormulaRepository.vega(SPOT, strike, TIME_TO_EXPIRY, vol, rate, 0.); final double resC2 = BlackScholesFormulaRepository.vega(SPOT, strike, TIME_TO_EXPIRY, vol, rate, 1.e12); final double refC2 = BlackScholesFormulaRepository.vega(SPOT, strike, TIME_TO_EXPIRY, vol, rate, inf); final double resC3 = BlackScholesFormulaRepository.vega(SPOT, strike, TIME_TO_EXPIRY, vol, rate, -1.e12); final double refC3 = BlackScholesFormulaRepository.vega(SPOT, strike, TIME_TO_EXPIRY, vol, rate, -inf); final double[] resVec = new double[] {resC1, resC2, resC3 }; final double[] refVec = new double[] {refC1, refC2, refC3 }; for (int k = 0; k < 3; ++k) { if (refVec[k] > 1.e10) { assertTrue(resVec[k] > 1.e10); } else { if (refVec[k] < -1.e10) { assertTrue(resVec[k] < -1.e10); } else { if (refVec[k] == 0.) { assertTrue(Math.abs(resVec[k]) < 1.e-10); } else { assertEquals(refVec[k], resVec[k], Math.max(Math.abs(refVec[k]) * 1.e-10, 1.e-10)); } } } } } } } final int nSpt = SPOT_EX.length; final int nStr = STRIKES_INPUT_EX.length; final int nTm = TIME_TO_EXPIRY_EX.length; final int nVlt = VOLS_EX.length; final int nRt = INTEREST_RATES_EX.length; final int nCst = COST_OF_CARRY_EX.length; for (int i = 0; i < nSpt; ++i) { final double spot = SPOT_EX[i]; for (int j = 0; j < nStr; ++j) { final double strike = STRIKES_INPUT_EX[j]; for (int l = 0; l < nTm; ++l) { final double time = TIME_TO_EXPIRY_EX[l]; for (int m = 0; m < nVlt; ++m) { final double vol = VOLS_EX[m]; for (int n = 0; n < nRt; ++n) { final double rate = INTEREST_RATES_EX[n]; for (int ii = 0; ii < nCst; ++ii) { final double cost = COST_OF_CARRY_EX[ii]; final double resC1 = BlackScholesFormulaRepository.vega(spot, strike, time, vol, rate, cost); assertTrue(!(Double.isNaN(resC1))); assertTrue(resC1 >= 0.); } } } } } } }
@Test(expectedExceptions = IllegalArgumentException.class) public void negativeVolErrorvegaTest() { BlackScholesFormulaRepository.vega(SPOT, STRIKES_INPUT[1], TIME_TO_EXPIRY, -0.5, INTEREST_RATES[1], COST_OF_CARRY); }
@Test(expectedExceptions = IllegalArgumentException.class) public void negativeSpotErrorvegaTest() { BlackScholesFormulaRepository.vega(-SPOT, STRIKES_INPUT[1], TIME_TO_EXPIRY, VOLS[1], INTEREST_RATES[1], COST_OF_CARRY); }
@Test(expectedExceptions = IllegalArgumentException.class) public void negativeStrikeErrorvegaTest() { BlackScholesFormulaRepository.vega(SPOT, -STRIKES_INPUT[1], TIME_TO_EXPIRY, VOLS[1], INTEREST_RATES[1], COST_OF_CARRY); }
@Test(expectedExceptions = IllegalArgumentException.class) public void negativeTimeErrorvegaTest() { BlackScholesFormulaRepository.vega(SPOT, STRIKES_INPUT[1], -TIME_TO_EXPIRY, VOLS[1], INTEREST_RATES[1], COST_OF_CARRY); }
@Test(expectedExceptions = IllegalArgumentException.class) public void nanIntErrorvegaTest() { BlackScholesFormulaRepository.vega(SPOT, STRIKES_INPUT[1], TIME_TO_EXPIRY, VOLS[1], NAN, COST_OF_CARRY); }
@Test(expectedExceptions = IllegalArgumentException.class) public void nanCarryErrorvegaTest() { BlackScholesFormulaRepository.vega(SPOT, STRIKES_INPUT[1], TIME_TO_EXPIRY, VOLS[1], INTEREST_RATES[1], NAN); }