IndexOptionPricer { public double getOptionPriceForSpreadQuotedIndex(final double defaultAdjIndexValue, final double vol, final double strike, final boolean isPayer) { ArgumentChecker.isTrue(strike >= 0.0, "strike cannot be negative"); final double x0 = calibrateX0(defaultAdjIndexValue, vol); final double gK = (strike - _coupon) * _annuityFunc.evaluate(strike); return optionPrice(defaultAdjIndexValue, x0, vol, gK, isPayer); } IndexOptionPricer(final CDSAnalytic fwdCDS, final double timeToExpiry, final ISDACompliantYieldCurve yieldCurve, final double coupon);  IndexOptionPricer(final CDSAnalytic fwdCDS, final double timeToExpiry, final ISDACompliantYieldCurve yieldCurve, final double coupon, final boolean useExactAnnuityCal); double getOptionPremium(final double defaultAdjIndexValue, final double vol, final IndexOptionStrike strike, final boolean isPayer); double getOptionPriceForSpreadQuotedIndex(final double defaultAdjIndexValue, final double vol, final double strike, final boolean isPayer); double getOptionPriceForPriceQuotedIndex(final double defaultAdjIndexValue, final double vol, final double gK, final boolean isPayer); double impliedVol(final double atmFwd, final double gK, final double price, final boolean isPayer); double calibrateX0(final double defaultAdjIndexValue, final double vol); Function1D<Double, Double> getDefaultAdjForwardForX(final double coupon);  }
@Test public void speedTest() { final CDSAnalytic spotCDS = FACTORY.makeIMMCDS(TRADE_DATE, Period.ofYears(5)); final double indexPrice = 0.021; final double f = 1.0; final IntrinsicIndexDataBundle adjCurves = PSA.adjustCurves(indexPrice, spotCDS, INDEX_COUPON, YIELD_CURVE, INTRINSIC_DATA); final LocalDate optionExpiry = TRADE_DATE.plusMonths(1); final double timeToExpiry = ACT365F.getDayCountFraction(TRADE_DATE, optionExpiry); final CDSAnalytic fwdStartCDS = FACTORY.makeForwardStartingIMMCDS(TRADE_DATE, optionExpiry, Period.ofYears(5)); final CDSAnalytic fwdCDS = FACTORY.makeIMMCDS(TRADE_DATE, Period.ofYears(5)); final IndexOptionPricer pricerISDA = new IndexOptionPricer(fwdCDS, timeToExpiry, YIELD_CURVE, INDEX_COUPON, true); final IndexOptionPricer pricerApprox = new IndexOptionPricer(fwdCDS, timeToExpiry, YIELD_CURVE, INDEX_COUPON); final double atmFwdVal = INDEX_CAL.defaultAdjustedForwardIndexValue(fwdStartCDS, timeToExpiry, YIELD_CURVE, INDEX_COUPON, adjCurves); if (PRINT) { final int warmups = 200; final int hotspots = 1000; for (int i = 0; i < warmups; i++) { final double vol = RANDOM.nextDouble(); final double k = 0.005 + 0.02 * RANDOM.nextDouble(); final double p = pricerISDA.getOptionPriceForSpreadQuotedIndex(atmFwdVal, vol, k, true); } long t0 = System.nanoTime(); for (int i = 0; i < hotspots; i++) { final double vol = RANDOM.nextDouble(); final double k = 0.005 + 0.02 * RANDOM.nextDouble(); final double p = pricerISDA.getOptionPriceForSpreadQuotedIndex(atmFwdVal, vol, k, true); } long t1 = System.nanoTime(); System.out.println("Time using ISDA for annuity: " + (t1 - t0) * 1e-6 / hotspots + "ms"); for (int i = 0; i < warmups; i++) { final double vol = RANDOM.nextDouble(); final double k = 0.005 + 0.02 * RANDOM.nextDouble(); final double p = pricerApprox.getOptionPriceForSpreadQuotedIndex(atmFwdVal, vol, k, true); } t0 = System.nanoTime(); for (int i = 0; i < hotspots; i++) { final double vol = RANDOM.nextDouble(); final double k = 0.005 + 0.02 * RANDOM.nextDouble(); final double p = pricerApprox.getOptionPriceForSpreadQuotedIndex(atmFwdVal, vol, k, true); } t1 = System.nanoTime(); System.out.println("Time using approximation for annuity: " + (t1 - t0) * 1e-6 / hotspots + "ms"); } }
@Test public void isdaVApproxTest() { final double indexPrice = 0.021; final CDSAnalytic spotCDS = FACTORY.makeIMMCDS(TRADE_DATE, Period.ofYears(5)); final IntrinsicIndexDataBundle adjCurves = PSA.adjustCurves(indexPrice, spotCDS, INDEX_COUPON, YIELD_CURVE, INTRINSIC_DATA); final LocalDate optionExpiry = TRADE_DATE.plusMonths(1); final double timeToExpiry = ACT365F.getDayCountFraction(TRADE_DATE, optionExpiry); final CDSAnalytic fwdStartCDS = FACTORY.makeForwardStartingIMMCDS(TRADE_DATE, optionExpiry, Period.ofYears(5)); final CDSAnalytic fwdCDS = FACTORY.makeIMMCDS(TRADE_DATE, Period.ofYears(5)); final IndexOptionPricer pricerISDA = new IndexOptionPricer(fwdCDS, timeToExpiry, YIELD_CURVE, INDEX_COUPON, true); final IndexOptionPricer pricerApprox = new IndexOptionPricer(fwdCDS, timeToExpiry, YIELD_CURVE, INDEX_COUPON); final double atmFwdVal = INDEX_CAL.defaultAdjustedForwardIndexValue(fwdStartCDS, timeToExpiry, YIELD_CURVE, INDEX_COUPON, adjCurves); final double vol = 0.4; for (int i = 0; i < 100; i++) { final double k = 0.002 + 0.02 * i / 100.; final double payer1 = pricerISDA.getOptionPriceForSpreadQuotedIndex(atmFwdVal, vol, k, true); final double payer2 = pricerApprox.getOptionPriceForSpreadQuotedIndex(atmFwdVal, vol, k, true); final double rec1 = pricerISDA.getOptionPriceForSpreadQuotedIndex(atmFwdVal, vol, k, false); final double rec2 = pricerApprox.getOptionPriceForSpreadQuotedIndex(atmFwdVal, vol, k, false); if (PRINT) { System.out.println(k + "\t" + payer1 + "\t" + payer2); } assertEquals(payer1, payer2, 1e-6 + payer1 * 1e-4); assertEquals(rec1, rec2, 1e-6 + rec1 * 1e-4); } }
@Test public void defaultedNamesTest() { final double indexPrice = 0.021; final double[] expOTM = new double[] {9.076800764955738E-58, 3.4226674270476496E-53, 3.421305456107505E-49, 1.1578431585829338E-45, 1.5971443685030326E-42, 1.0368924201927302E-39, 3.5488934506809077E-37, 7.013017170443694E-35, 8.614686159820895E-33, 6.989713086397245E-31, 3.9397404998361574E-29, 1.6092967109704648E-27, 4.937503232924944E-26, 1.1730934910278846E-24, 2.2155898144875945E-23, 3.4025886679708334E-22, 4.333505166418396E-21, 4.6563803518661164E-20, 4.2854645659737556E-19, 3.4235577931791882E-18, 2.402256063322698E-17, 1.4961984880648425E-16, 8.349609439174132E-16, 4.210233474792479E-15, 1.9328458757406006E-14, 8.134023739062972E-14, 3.15729001261722E-13, 1.1367368714819529E-12, 3.815542543731805E-12, 1.1995608173294043E-11, 3.547357283025416E-11, 9.905947198985513E-11, 2.62149200845133E-10, 6.59616065568106E-10, 1.582854456644385E-9, 3.63256974036388E-9, 7.993468395411526E-9, 1.6906724004197517E-8, 3.444534406470765E-8, 6.774232294873387E-8, 1.2885050063100898E-7, 2.374612436930876E-7, 4.2472177780647457E-7, 7.384252217101014E-7, 1.2497928944235257E-6, 2.0620462115765177E-6, 3.3208584405433833E-6, 5.22664910774526E-6, 8.048489915315826E-6, 1.2139283310038822E-5, 1.7951546138525213E-5, 2.6053032280397427E-5, 3.7141442120650334E-5, 5.2056601696062696E-5, 7.179001552326956E-5, 9.748982249311326E-5, 1.3046086022755916E-4, 1.7215897710912286E-4, 2.2417926032948268E-4, 2.882381110269799E-4, 3.6614946597432273E-4, 4.5979578000882737E-4, 5.710947311635238E-4, 7.019627942388466E-4, 8.542770685297848E-4, 0.0010298368141732719, 0.001230326139299173, 0.0014572792580807967, 0.0017120495336512811, 0.0019957833508774444, 0.0023093995935475906, 0.0026535752237636084, 0.0030287371954614726, 0.0034350606417693345, 0.003872473042891695, 0.004273779463476312, 0.003854085486068055, 0.0034654428042082794, 0.0031069260932821844, 0.0027774504027998047, 0.0024757964184719925, 0.0022006362856210955, 0.0019505592571228772, 0.0017240964970243375, 0.0015197445557299247, 0.001335986936817134, 0.0011713136864685823, 0.0010242385494673638, 8.93313737411912E-4, 7.771422330342129E-4, 6.743876948715313E-4, 5.837820728043969E-4, 5.041310934645091E-4, 4.343178207319807E-4, 3.733044819095235E-4, 3.2013281811392165E-4, 2.7392309091215267E-4, 2.338722206547276E-4, 1.9925083793707264E-4, 1.693998770868527E-4 }; final CDSAnalytic spotCDS = FACTORY.makeIMMCDS(TRADE_DATE, Period.ofYears(5)); final IntrinsicIndexDataBundle adjCurves = PSA.adjustCurves(indexPrice, spotCDS, INDEX_COUPON, YIELD_CURVE, INTRINSIC_DATA); final LocalDate optionExpiry = TRADE_DATE.plusMonths(1); final double timeToExpiry = ACT365F.getDayCountFraction(TRADE_DATE, optionExpiry); final CDSAnalytic fwdStartCDS = FACTORY.makeForwardStartingIMMCDS(TRADE_DATE, optionExpiry, Period.ofYears(5)); final CDSAnalytic fwdCDS = FACTORY.makeIMMCDS(TRADE_DATE, Period.ofYears(5)); final IndexOptionPricer pricerISDA = new IndexOptionPricer(fwdCDS, timeToExpiry, YIELD_CURVE, INDEX_COUPON, true); final IndexOptionPricer pricerApprox = new IndexOptionPricer(fwdCDS, timeToExpiry, YIELD_CURVE, INDEX_COUPON); final IntrinsicIndexDataBundle adjCurvesWithDefaults = adjCurves.withDefault(0, 34, 111); final double atmFwdVal = INDEX_CAL.defaultAdjustedForwardIndexValue(fwdStartCDS, timeToExpiry, YIELD_CURVE, INDEX_COUPON, adjCurvesWithDefaults); final double atmK = (new MarketQuoteConverter()).pufToQuotedSpread(fwdCDS, INDEX_COUPON, YIELD_CURVE.withOffset(timeToExpiry), atmFwdVal); final double vol = 0.5; for (int i = 0; i < 100; i++) { final double k = 0.002 + 0.02 * i / 100.; final double payer1 = pricerISDA.getOptionPriceForSpreadQuotedIndex(atmFwdVal, vol, k, true); final double payer2 = pricerApprox.getOptionPriceForSpreadQuotedIndex(atmFwdVal, vol, k, true); final double rec1 = pricerISDA.getOptionPriceForSpreadQuotedIndex(atmFwdVal, vol, k, false); final double rec2 = pricerApprox.getOptionPriceForSpreadQuotedIndex(atmFwdVal, vol, k, false); final double otm = pricerISDA.getOptionPriceForSpreadQuotedIndex(atmFwdVal, vol, k, k >= atmK); if (PRINT) { System.out.println(k + "\t" + payer1 + "\t" + payer2); } assertEquals(payer1, payer2, 1e-6 + payer1 * 1e-4); assertEquals(rec1, rec2, 1e-6 + rec1 * 1e-4); assertEquals(expOTM[i], otm, expOTM[i] * 1e-12); } }