@Test public void consistencyTest() { final double tol = 1.e-12; final LocalDate optionExpiry = getNextIMMDate(TRADE_DATE).minusDays(1); final double timeToExpiry = ACT365F.yearFraction(TRADE_DATE, optionExpiry); final CDSAnalytic fwdCDX = FACTORY.makeCDX(optionExpiry, Period.ofYears(5)); final CDSAnalytic fwdStartingCDX = fwdCDX.withOffset(timeToExpiry); final double[] indexPUF = new double[] {0.0556, 0.0582, 0.0771, 0.0652}; final CDSAnalytic[] indexCDS = FACTORY.makeCDX(TRADE_DATE, INDEX_PILLARS); final IntrinsicIndexDataBundle adjCurves = PSA.adjustCurves(indexPUF, indexCDS, INDEX_COUPON, YIELD_CURVE, INTRINSIC_DATA); final double fwdSpread = INDEX_CAL.defaultAdjustedForwardSpread( fwdStartingCDX, timeToExpiry, YIELD_CURVE, adjCurves); final double fwdAnnuity = INDEX_CAL.indexAnnuity(fwdStartingCDX, YIELD_CURVE, adjCurves); final BlackIndexOptionPricer pricerWithFwd = new BlackIndexOptionPricer( fwdCDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, fwdSpread, fwdAnnuity); final BlackIndexOptionPricer pricerNoFwd = new BlackIndexOptionPricer(fwdCDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, adjCurves); final boolean[] payer = new boolean[] {true, false}; final double vol = 0.4; final double strikeSpread = 0.015; final ISDACompliantYieldCurve fwdYC = YIELD_CURVE.withOffset(timeToExpiry); final double strikePrice = CONVERTER.quotedSpreadToPUF(fwdCDX, INDEX_COUPON, fwdYC, strikeSpread); final IndexOptionStrike exPriceAmount = new ExerciseAmount(strikePrice); final IndexOptionStrike exSpreadAmount = new SpreadBasedStrike(strikeSpread); for (int i = 0; i < 2; ++i) { /** Consistency between option pricing methods */ final double premFromPrice = pricerWithFwd.getOptionPremium(exPriceAmount, vol, payer[i]); final double premFromSpread = pricerWithFwd.getOptionPremium(exSpreadAmount, vol, payer[i]); final double premFromPriceBare = pricerWithFwd.getOptionPriceForPriceQuotedIndex(strikePrice, vol, payer[i]); final double premFromSpreadBare = pricerWithFwd.getOptionPriceForSpreadQuotedIndex(strikeSpread, vol, payer[i]); assertEquals(premFromPrice, premFromSpread, tol); assertEquals(premFromSpread, premFromPriceBare, tol); assertEquals(premFromPriceBare, premFromSpreadBare, tol); /** Consistency between constructors */ final double PremFromPriceNoFwd = pricerNoFwd.getOptionPremium(exPriceAmount, vol, payer[i]); assertEquals(premFromPrice, PremFromPriceNoFwd, tol); /** Consistency with implied vol */ final double vol1 = pricerWithFwd.getImpliedVolatility(exPriceAmount, premFromPrice, payer[i]); final double vol2 = pricerWithFwd.getImpliedVolatility(exSpreadAmount, premFromSpread, payer[i]); final double vol3 = pricerWithFwd.getImpliedVolForExercisePrice(strikePrice, premFromPriceBare, payer[i]); final double vol4 = pricerWithFwd.getImpliedVolForSpreadStrike(strikeSpread, premFromSpreadBare, payer[i]); assertEquals(vol, vol1, tol); assertEquals(vol, vol2, tol); assertEquals(vol, vol3, tol); assertEquals(vol, vol4, tol); } }
@Override public Set<ComputedValue> execute( final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) throws AsynchronousExecution { final ZonedDateTime now = ZonedDateTime.now(executionContext.getValuationClock()); final ValueRequirement requirement = desiredValues.iterator().next(); final ValueProperties properties = requirement.getConstraints().copy().get(); final LegacyVanillaCDSSecurity security = (LegacyVanillaCDSSecurity) target.getSecurity(); // LegacyVanillaCreditDefaultSwapDefinition cds = // _converter.visitLegacyVanillaCDSSecurity(security); final ValueRequirement desiredValue = desiredValues.iterator().next(); // all same constraints final String quoteConventionString = desiredValue.getConstraint(ISDAFunctionConstants.CDS_QUOTE_CONVENTION); final StandardCDSQuotingConvention quoteConvention = StandardCDSQuotingConvention.parse(quoteConventionString); final CdsRecoveryRateIdentifier recoveryRateIdentifier = security.accept( new CreditSecurityToRecoveryRateVisitor(executionContext.getSecuritySource())); Object recoveryRateObject = inputs.getValue( new ValueRequirement( "PX_LAST", ComputationTargetType.PRIMITIVE, recoveryRateIdentifier.getExternalId())); if (recoveryRateObject == null) { throw new OpenGammaRuntimeException("Could not get recovery rate"); // s_logger.warn("Could not get recovery rate, defaulting to 0.4: " + recoveryRateIdentifier); // recoveryRateObject = 0.4; } final double recoveryRate = (Double) recoveryRateObject; // get the isda curve final Object isdaObject = inputs.getValue(ValueRequirementNames.YIELD_CURVE); if (isdaObject == null) { throw new OpenGammaRuntimeException("Couldn't get isda curve"); } final ISDACompliantYieldCurve yieldCurve = (ISDACompliantYieldCurve) isdaObject; // spreads NodalTenorDoubleCurve spreadObject = (NodalTenorDoubleCurve) inputs.getValue(ValueRequirementNames.BUCKETED_SPREADS); if (spreadObject == null) { throw new OpenGammaRuntimeException("Unable to get spreads"); } final double[] spreads = ArrayUtils.toPrimitive(spreadObject.getYData()); // final String pillarString = IMMDateGenerator.isIMMDate(security.getMaturityDate()) ? // requirement.getConstraint(ISDAFunctionConstants.ISDA_BUCKET_TENORS) : // ISDACompliantCreditCurveFunction.NON_IMM_PILLAR_TENORS; final ZonedDateTime[] bucketDates = SpreadCurveFunctions.getPillarDates(now, spreadObject.getXData()); final CDSQuoteConvention[] quotes = SpreadCurveFunctions.getQuotes( security.getMaturityDate(), spreads, security.getParSpread(), quoteConvention, false); // spreads NodalTenorDoubleCurve pillarObject = (NodalTenorDoubleCurve) inputs.getValue(ValueRequirementNames.PILLAR_SPREADS); if (pillarObject == null) { throw new OpenGammaRuntimeException("Unable to get pillars"); } // CDS analytics for credit curve (possible performance improvement if earlier result obtained) // final LegacyVanillaCreditDefaultSwapDefinition curveCDS = cds.withStartDate(now); // security.setStartDate(now); // needed for curve instruments final CDSAnalytic[] bucketCDSs = new CDSAnalytic[bucketDates.length]; for (int i = 0; i < bucketCDSs.length; i++) { // security.setMaturityDate(bucketDates[i]); final CDSAnalyticVisitor visitor = new CDSAnalyticVisitor( now.toLocalDate(), _holidaySource, _regionSource, security.getStartDate().toLocalDate(), bucketDates[i].toLocalDate(), recoveryRate); bucketCDSs[i] = security.accept(visitor); } final ZonedDateTime[] pillarDates = SpreadCurveFunctions.getPillarDates(now, pillarObject.getXData()); final CDSAnalytic[] pillarCDSs = new CDSAnalytic[pillarDates.length]; for (int i = 0; i < pillarCDSs.length; i++) { // security.setMaturityDate(bucketDates[i]); final CDSAnalyticVisitor visitor = new CDSAnalyticVisitor( now.toLocalDate(), _holidaySource, _regionSource, security.getStartDate().toLocalDate(), pillarDates[i].toLocalDate(), recoveryRate); pillarCDSs[i] = security.accept(visitor); } final ISDACompliantCreditCurve creditCurve = (ISDACompliantCreditCurve) inputs.getValue(ValueRequirementNames.HAZARD_RATE_CURVE); if (creditCurve == null) { throw new OpenGammaRuntimeException("Couldnt get credit curve"); } // final CDSAnalytic analytic = CDSAnalyticConverter.create(cds, now.toLocalDate()); final CDSAnalyticVisitor visitor = new CDSAnalyticVisitor(now.toLocalDate(), _holidaySource, _regionSource, recoveryRate); final CDSAnalytic analytic = security.accept(visitor); final BuySellProtection buySellProtection = security.isBuy() ? BuySellProtection.BUY : BuySellProtection.SELL; // final String term = new Tenor(Period.between(security.getStartDate().toLocalDate(), // security.getMaturityDate().toLocalDate())).getPeriod().toString(); // final Double cdsQuoteDouble = (Double) inputs.getValue(new // ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, // ComputationTargetType.PRIMITIVE, ExternalId.of("Tenor", term))); final Double cdsQuoteDouble = (Double) inputs.getValue(MarketDataRequirementNames.MARKET_VALUE); if (cdsQuoteDouble == null) { throw new OpenGammaRuntimeException("Couldn't get spread for " + security); } final CDSQuoteConvention quote = SpreadCurveFunctions.getQuotes( security.getMaturityDate(), new double[] {cdsQuoteDouble}, security.getParSpread(), quoteConvention, true)[0]; boolean isNonIMMFAndFromPUF = !IMMDateLogic.isIMMDate(security.getMaturityDate().toLocalDate()) && quote instanceof PointsUpFront; boolean isNonIMMAndFromSpread = !IMMDateLogic.isIMMDate(security.getMaturityDate().toLocalDate()) && (quote instanceof QuotedSpread || quote instanceof ParSpread); int buySellPremiumFactor = security.isBuy() ? -1 : 1; final double notional = security.getNotional().getAmount(); final double coupon = security.getParSpread() * ONE_BPS; final PointsUpFront puf = getPointsUpfront(quote, buySellProtection, yieldCurve, analytic, creditCurve); final double accruedPremiumPrim = isNonIMMAndFromSpread || isNonIMMFAndFromPUF ? 0 : analytic.getAccruedPremium(coupon); // final double accruedPremium = isNonIMMAndFromSpread || isNonIMMFAndFromPUF ? 0 : // analytic.getAccruedPremium(coupon) * notional * buySellPremiumFactor; final double accruedPremium = isNonIMMAndFromSpread || isNonIMMFAndFromPUF ? 0 : accruedPremiumPrim * notional * buySellPremiumFactor; final int accruedDays = isNonIMMAndFromSpread || isNonIMMFAndFromPUF ? 0 : analytic.getAccuredDays(); final double quotedSpread = getQuotedSpread(quote, puf, buySellProtection, yieldCurve, analytic).getQuotedSpread(); final double upfrontAmount = isNonIMMAndFromSpread ? 0 : getUpfrontAmount(analytic, puf, notional, accruedPremiumPrim, buySellProtection); final double cleanPV = puf.getPointsUpFront() * notional; final double principal = isNonIMMAndFromSpread ? 0 : cleanPV; final double cleanPrice = getCleanPrice(puf); final TenorLabelledMatrix1D bucketedCS01 = getBucketedCS01( analytic, bucketCDSs, spreadObject.getXData(), quote, notional, yieldCurve, creditCurve); final double parallelCS01 = getParallelCS01( quote, analytic, yieldCurve, notional, pillarCDSs, ArrayUtils.toPrimitive(pillarObject.getYData())); final Set<ComputedValue> results = Sets.newHashSetWithExpectedSize(_valueRequirements.length); results.add( new ComputedValue( new ValueSpecification( ValueRequirementNames.ACCRUED_PREMIUM, target.toSpecification(), properties), accruedPremium)); results.add( new ComputedValue( new ValueSpecification( ValueRequirementNames.ACCRUED_DAYS, target.toSpecification(), properties), accruedDays)); results.add( new ComputedValue( new ValueSpecification( ValueRequirementNames.QUOTED_SPREAD, target.toSpecification(), properties), quotedSpread / ONE_BPS)); results.add( new ComputedValue( new ValueSpecification( ValueRequirementNames.UPFRONT_AMOUNT, target.toSpecification(), properties), upfrontAmount)); results.add( new ComputedValue( new ValueSpecification( ValueRequirementNames.DIRTY_PRESENT_VALUE, target.toSpecification(), properties), upfrontAmount)); results.add( new ComputedValue( new ValueSpecification( ValueRequirementNames.CLEAN_PRESENT_VALUE, target.toSpecification(), properties), cleanPV)); results.add( new ComputedValue( new ValueSpecification( ValueRequirementNames.PRINCIPAL, target.toSpecification(), properties), principal)); results.add( new ComputedValue( new ValueSpecification( ValueRequirementNames.CLEAN_PRICE, target.toSpecification(), properties), cleanPrice)); results.add( new ComputedValue( new ValueSpecification( ValueRequirementNames.BUCKETED_CS01, target.toSpecification(), properties), bucketedCS01)); results.add( new ComputedValue( new ValueSpecification( ValueRequirementNames.PARALLEL_CS01, target.toSpecification(), properties), parallelCS01)); results.add( new ComputedValue( new ValueSpecification( ValueRequirementNames.POINTS_UPFRONT, target.toSpecification(), properties), puf.getPointsUpFront())); return results; }
@SuppressWarnings("unused") @Test public void limitTest() { final LocalDate optionExpiry = getNextIMMDate(TRADE_DATE).minusDays(1); final double timeToExpiry = ACT365F.yearFraction(TRADE_DATE, optionExpiry); final CDSAnalytic fwdCDX = FACTORY.makeCDX(optionExpiry, Period.ofYears(5)); final CDSAnalytic fwdStartingCDX = fwdCDX.withOffset(timeToExpiry); final double[] indexPUF = new double[] {0.0556, 0.0582, 0.0771, 0.0652}; final CDSAnalytic[] indexCDS = FACTORY.makeCDX(TRADE_DATE, INDEX_PILLARS); final IntrinsicIndexDataBundle adjCurves = PSA.adjustCurves(indexPUF, indexCDS, INDEX_COUPON, YIELD_CURVE, INTRINSIC_DATA); final double fwdSpread = INDEX_CAL.defaultAdjustedForwardSpread( fwdStartingCDX, timeToExpiry, YIELD_CURVE, adjCurves); final double fwdAnnuity = INDEX_CAL.indexAnnuity(fwdStartingCDX, YIELD_CURVE, adjCurves); final BlackIndexOptionPricer pricerWithFwd = new BlackIndexOptionPricer( fwdCDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, fwdSpread, fwdAnnuity); final double modStrikeLimit = INDEX_COUPON + fwdCDX.getLGD() / fwdAnnuity; final double vol = 0.4; final double payLargeSpLimit = fwdAnnuity * BlackFormulaRepository.price(fwdSpread, modStrikeLimit, timeToExpiry, vol, true); final double recLargeSpLimit = fwdAnnuity * BlackFormulaRepository.price(fwdSpread, modStrikeLimit, timeToExpiry, vol, false); final double largeStrikeSp = 75.0; final double payLargeStrikeSp = pricerWithFwd.getOptionPriceForSpreadQuotedIndex(largeStrikeSp, vol, true); final double recLargeStrikeSp = pricerWithFwd.getOptionPriceForSpreadQuotedIndex(largeStrikeSp, vol, false); assertEquals(payLargeSpLimit, payLargeStrikeSp, 1.e-12); assertEquals( recLargeSpLimit, recLargeStrikeSp, 1.e-3); // larger strike spread ends up with failure in root-finding /** Exception expected */ final double negativeTime = -0.5; try { new BlackIndexOptionPricer( fwdCDX, negativeTime, YIELD_CURVE, INDEX_COUPON, fwdSpread, fwdAnnuity); throw new RuntimeException(); } catch (final Exception e) { assertEquals("timeToExpiry must be positive. Value given " + negativeTime, e.getMessage()); } try { new BlackIndexOptionPricer( fwdStartingCDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, fwdSpread, fwdAnnuity); throw new RuntimeException(); } catch (final Exception e) { assertEquals("fwdCDS should be a Forward CDS", e.getMessage()); } final double negativeCoupon = -150.0 * 1.0e-4; try { new BlackIndexOptionPricer( fwdCDX, timeToExpiry, YIELD_CURVE, negativeCoupon, fwdSpread, fwdAnnuity); throw new RuntimeException(); } catch (final Exception e) { assertEquals("indexCoupon must be positive", e.getMessage()); } final double negativeFwdSp = -0.5; try { new BlackIndexOptionPricer( fwdCDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, negativeFwdSp, fwdAnnuity); throw new RuntimeException(); } catch (final Exception e) { assertEquals("defaultAdjustedFwdSpread must be positive", e.getMessage()); } final double negativeAnn = -0.3; try { new BlackIndexOptionPricer( fwdCDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, fwdSpread, negativeAnn); throw new RuntimeException(); } catch (final Exception e) { assertEquals("pvFwdAnnuity must be positive", e.getMessage()); } final double largeAnn = fwdCDX.getProtectionEnd() * 2.0; try { new BlackIndexOptionPricer( fwdCDX, timeToExpiry, YIELD_CURVE, INDEX_COUPON, fwdSpread, largeAnn); throw new RuntimeException(); } catch (final Exception e) { assertEquals( "Value of annuity of " + largeAnn + " is greater than length (in years) of forward CDS. Annuity should be given for unit notional", e.getMessage()); } final double smallStrike = -0.9; try { pricerWithFwd.getOptionPriceForPriceQuotedIndex(smallStrike, vol, true); throw new RuntimeException(); } catch (final Exception e) { assertTrue(e instanceof IllegalArgumentException); } final double largeStrike = 0.9; try { pricerWithFwd.getOptionPriceForPriceQuotedIndex(largeStrike, vol, true); throw new RuntimeException(); } catch (final Exception e) { assertTrue(e instanceof IllegalArgumentException); } try { pricerWithFwd.getOptionPriceForSpreadQuotedIndex(smallStrike, vol, true); throw new RuntimeException(); } catch (final Exception e) { assertTrue(e instanceof IllegalArgumentException); } }