BlackScholesFormulaRepository { @ExternalFunction public static double vomma(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"); final double rootT = Math.sqrt(timeToExpiry); double sigmaRootT = lognormalVol * rootT; if (Double.isNaN(sigmaRootT)) { sigmaRootT = 1.; } if (spot > LARGE * strike || strike > LARGE * spot || rootT < SMALL) { return 0.; } double d1 = 0.; double d1d2Mod = 0.; if (Math.abs(spot - strike) < SMALL || (spot > LARGE && strike > LARGE) || rootT > LARGE) { final double costOvVol = (Math.abs(costOfCarry) < SMALL && lognormalVol < SMALL) ? Math.signum(costOfCarry) : costOfCarry / lognormalVol; final double coefD1 = costOvVol + 0.5 * lognormalVol; final double coefD1D2Mod = costOvVol * costOvVol / lognormalVol - 0.25 * lognormalVol; final double tmpD1 = coefD1 * rootT; final double tmpD1d2Mod = coefD1D2Mod * rootT * timeToExpiry; d1 = Double.isNaN(tmpD1) ? 0. : tmpD1; d1d2Mod = Double.isNaN(tmpD1d2Mod) ? 1. : tmpD1d2Mod; } else { if (lognormalVol > LARGE) { d1 = 0.5 * sigmaRootT; d1d2Mod = -0.25 * sigmaRootT * timeToExpiry; } else { if (lognormalVol < SMALL) { final double d1Tmp = (Math.log(spot / strike) / rootT + costOfCarry * rootT) / lognormalVol; d1 = Double.isNaN(d1Tmp) ? 1. : d1Tmp; d1d2Mod = d1 * d1 * rootT / lognormalVol; } else { final double tmp = Math.log(spot / strike) / sigmaRootT + costOfCarry * rootT / lognormalVol; d1 = tmp + 0.5 * sigmaRootT; d1d2Mod = (tmp * tmp - 0.25 * sigmaRootT * sigmaRootT) * rootT / lognormalVol; } } } 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. : (d1d2Mod >= 0. ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY); } if (-rate > LARGE) { return 0.; } coef = Math.exp(rate * timeToExpiry); } final double norm = NORMAL.getPDF(d1); double tmp = d1d2Mod * spot * coef; if (Double.isNaN(tmp)) { tmp = coef; } return norm < SMALL ? 0. : norm * tmp; }  @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 exvommaTest() { 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.vomma(1.e-12 * strike, strike, TIME_TO_EXPIRY, vol, rate, COST_OF_CARRY); final double refC1 = BlackScholesFormulaRepository.vomma(0., strike, TIME_TO_EXPIRY, vol, rate, COST_OF_CARRY); final double resC2 = BlackScholesFormulaRepository.vomma(1.e12 * strike, strike, TIME_TO_EXPIRY, vol, rate, COST_OF_CARRY); final double refC2 = BlackScholesFormulaRepository.vomma(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.vomma(SPOT, 1.e-12 * SPOT, TIME_TO_EXPIRY, vol, rate, COST_OF_CARRY); final double resC2 = BlackScholesFormulaRepository.vomma(SPOT, 1.e12 * SPOT, TIME_TO_EXPIRY, vol, rate, COST_OF_CARRY); final double refC1 = BlackScholesFormulaRepository.vomma(SPOT, 0., TIME_TO_EXPIRY, vol, rate, COST_OF_CARRY); final double refC2 = BlackScholesFormulaRepository.vomma(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.vomma(SPOT, strike, 1e-24, vol, rate, COST_OF_CARRY); final double resC2 = BlackScholesFormulaRepository.vomma(SPOT, strike, 1e24, vol, rate, COST_OF_CARRY); final double refC1 = BlackScholesFormulaRepository.vomma(SPOT, strike, 0., vol, rate, COST_OF_CARRY); final double refC2 = BlackScholesFormulaRepository.vomma(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.vomma(SPOT, strike, TIME_TO_EXPIRY, 1.e-12, rate, COST_OF_CARRY); final double refC1 = BlackScholesFormulaRepository.vomma(SPOT, strike, TIME_TO_EXPIRY, 0., rate, COST_OF_CARRY); final double resC2 = BlackScholesFormulaRepository.vomma(SPOT, strike, TIME_TO_EXPIRY, 1.e12, rate, COST_OF_CARRY); final double refC2 = BlackScholesFormulaRepository.vomma(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.vomma(SPOT, strike, TIME_TO_EXPIRY, vol, 1.e-12, COST_OF_CARRY); final double refC1 = BlackScholesFormulaRepository.vomma(SPOT, strike, TIME_TO_EXPIRY, vol, 0., COST_OF_CARRY); final double resC2 = BlackScholesFormulaRepository.vomma(SPOT, strike, TIME_TO_EXPIRY, vol, 1.e12, COST_OF_CARRY); final double refC2 = BlackScholesFormulaRepository.vomma(SPOT, strike, TIME_TO_EXPIRY, vol, inf, COST_OF_CARRY); final double resC3 = BlackScholesFormulaRepository.vomma(SPOT, strike, TIME_TO_EXPIRY, vol, -1.e12, COST_OF_CARRY); final double refC3 = BlackScholesFormulaRepository.vomma(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.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.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.vomma(SPOT, strike, TIME_TO_EXPIRY, vol, rate, 1.e-12); final double refC1 = BlackScholesFormulaRepository.vomma(SPOT, strike, TIME_TO_EXPIRY, vol, rate, 0.); final double resC2 = BlackScholesFormulaRepository.vomma(SPOT, strike, TIME_TO_EXPIRY, vol, rate, 1.e12); final double refC2 = BlackScholesFormulaRepository.vomma(SPOT, strike, TIME_TO_EXPIRY, vol, rate, inf); final double resC3 = BlackScholesFormulaRepository.vomma(SPOT, strike, TIME_TO_EXPIRY, vol, rate, -1.e12); final double refC3 = BlackScholesFormulaRepository.vomma(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-8, 1.e-8)); } } } } } } } 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.vomma(spot, strike, time, vol, rate, cost); assertTrue(!(Double.isNaN(resC1))); } } } } } } }
@Test(expectedExceptions = IllegalArgumentException.class) public void negativeVolErrorvommaTest() { BlackScholesFormulaRepository.vomma(SPOT, STRIKES_INPUT[1], TIME_TO_EXPIRY, -0.5, INTEREST_RATES[1], COST_OF_CARRY); }
@Test(expectedExceptions = IllegalArgumentException.class) public void negativeSpotErrorvommaTest() { BlackScholesFormulaRepository.vomma(-SPOT, STRIKES_INPUT[1], TIME_TO_EXPIRY, VOLS[1], INTEREST_RATES[1], COST_OF_CARRY); }
@Test(expectedExceptions = IllegalArgumentException.class) public void negativeStrikeErrorvommaTest() { BlackScholesFormulaRepository.vomma(SPOT, -STRIKES_INPUT[1], TIME_TO_EXPIRY, VOLS[1], INTEREST_RATES[1], COST_OF_CARRY); }
@Test(expectedExceptions = IllegalArgumentException.class) public void negativeTimeErrorvommaTest() { BlackScholesFormulaRepository.vomma(SPOT, STRIKES_INPUT[1], -TIME_TO_EXPIRY, VOLS[1], INTEREST_RATES[1], COST_OF_CARRY); }
@Test(expectedExceptions = IllegalArgumentException.class) public void nanIntErrorvommaTest() { BlackScholesFormulaRepository.vomma(SPOT, STRIKES_INPUT[1], TIME_TO_EXPIRY, VOLS[1], NAN, COST_OF_CARRY); }
@Test(expectedExceptions = IllegalArgumentException.class) public void nanCarryErrorvommaTest() { BlackScholesFormulaRepository.vomma(SPOT, STRIKES_INPUT[1], TIME_TO_EXPIRY, VOLS[1], INTEREST_RATES[1], NAN); }