Java tutorial
/* * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.analytics.model.credit.isdanew; import java.util.Collection; import java.util.Collections; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import org.apache.commons.lang.ArrayUtils; import org.threeten.bp.Clock; import org.threeten.bp.Period; import org.threeten.bp.ZoneId; import org.threeten.bp.ZonedDateTime; import com.google.common.collect.Sets; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.analytics.financial.credit.BuySellProtection; import com.opengamma.analytics.financial.credit.creditdefaultswap.StandardCDSQuotingConvention; import com.opengamma.analytics.financial.credit.creditdefaultswap.definition.legacy.LegacyVanillaCreditDefaultSwapDefinition; import com.opengamma.analytics.financial.credit.creditdefaultswap.pricing.vanilla.isdanew.CDSAnalytic; import com.opengamma.analytics.financial.credit.creditdefaultswap.pricing.vanilla.isdanew.CDSAnalyticFactory; import com.opengamma.analytics.financial.credit.creditdefaultswap.pricing.vanilla.isdanew.CDSQuoteConvention; import com.opengamma.analytics.financial.credit.creditdefaultswap.pricing.vanilla.isdanew.FastCreditCurveBuilder; import com.opengamma.analytics.financial.credit.creditdefaultswap.pricing.vanilla.isdanew.ISDACompliantCreditCurve; import com.opengamma.analytics.financial.credit.creditdefaultswap.pricing.vanilla.isdanew.ISDACompliantYieldCurve; import com.opengamma.analytics.financial.credit.creditdefaultswap.pricing.vanilla.isdanew.ParSpread; import com.opengamma.analytics.financial.credit.creditdefaultswap.pricing.vanilla.isdanew.PointsUpFront; import com.opengamma.analytics.financial.credit.creditdefaultswap.pricing.vanilla.isdanew.PointsUpFrontConverter; import com.opengamma.analytics.financial.credit.creditdefaultswap.pricing.vanilla.isdanew.QuotedSpread; import com.opengamma.analytics.math.curve.NodalTenorDoubleCurve; import com.opengamma.core.AbstractSourceWithExternalBundle; import com.opengamma.core.change.ChangeManager; import com.opengamma.core.change.DummyChangeManager; import com.opengamma.core.holiday.HolidaySource; import com.opengamma.core.holiday.impl.WeekendHolidaySource; import com.opengamma.core.region.Region; import com.opengamma.core.region.RegionSource; import com.opengamma.core.value.MarketDataRequirementNames; import com.opengamma.engine.ComputationTarget; import com.opengamma.engine.function.AbstractFunction; import com.opengamma.engine.function.FunctionCompilationContext; import com.opengamma.engine.function.FunctionExecutionContext; import com.opengamma.engine.function.FunctionInputs; import com.opengamma.engine.target.ComputationTargetType; import com.opengamma.engine.value.ComputedValue; import com.opengamma.engine.value.ValueProperties; import com.opengamma.engine.value.ValuePropertyNames; import com.opengamma.engine.value.ValueRequirement; import com.opengamma.engine.value.ValueRequirementNames; import com.opengamma.engine.value.ValueSpecification; import com.opengamma.financial.analytics.conversion.CreditDefaultSwapSecurityConverterDeprecated; import com.opengamma.financial.analytics.model.cds.ISDAFunctionConstants; import com.opengamma.financial.analytics.model.credit.CreditSecurityToRecoveryRateVisitor; import com.opengamma.financial.analytics.model.credit.IMMDateGenerator; import com.opengamma.financial.analytics.model.credit.SpreadCurveFunctions; import com.opengamma.financial.credit.CdsRecoveryRateIdentifier; import com.opengamma.financial.security.FinancialSecurityTypes; import com.opengamma.financial.security.cds.CreditDefaultSwapSecurity; import com.opengamma.financial.security.cds.LegacyVanillaCDSSecurity; import com.opengamma.id.ExternalId; import com.opengamma.id.ExternalIdBundle; import com.opengamma.id.ObjectId; import com.opengamma.id.UniqueId; import com.opengamma.id.VersionCorrection; import com.opengamma.master.region.ManageableRegion; import com.opengamma.util.async.AsynchronousExecution; import com.opengamma.util.credit.CreditCurveIdentifier; import com.opengamma.util.i18n.Country; import com.opengamma.util.money.Currency; import com.opengamma.util.time.Tenor; /** Function to return spreads modified for a given security */ public class ISDACompliantCreditCurveFunction extends AbstractFunction.NonCompiledInvoker { /** String representation of fixed pillars used for non IMM */ public static final String NON_IMM_PILLAR_TENORS = "P6M,P1Y,P2Y,P3Y,P4Y,P5Y,P7Y,P10Y"; private static final FastCreditCurveBuilder CREDIT_CURVE_BUILDER = new FastCreditCurveBuilder(); private static final PointsUpFrontConverter POINTS_UP_FRONT_CONVERTER = new PointsUpFrontConverter(); private HolidaySource _holidaySource; private RegionSource _regionSource; public static CreditCurveIdentifier getSpreadCurveIdentifier(final CreditDefaultSwapSecurity cds) { return getCreditCurveIdentifier(cds, ""); } public static CreditCurveIdentifier getISDACurveIdentifier(final CreditDefaultSwapSecurity cds) { return getCreditCurveIdentifier(cds, "ISDA_"); } /** * Get the CreditCurveIdentifier with name appended * * @param security */ private static CreditCurveIdentifier getCreditCurveIdentifier(final CreditDefaultSwapSecurity security, final String name) { final CreditCurveIdentifier curveIdentifier = CreditCurveIdentifier.of( name + security.getReferenceEntity().getValue(), security.getNotional().getCurrency(), security.getDebtSeniority().toString(), security.getRestructuringClause().toString()); return curveIdentifier; } public static QuotedSpread getQuotedSpread(CDSQuoteConvention quote, BuySellProtection buySellProtection, ISDACompliantYieldCurve yieldCurve, CDSAnalytic analytic, double premium) { double quotedSpread; if (quote instanceof PointsUpFront) { quotedSpread = POINTS_UP_FRONT_CONVERTER.pufToQuotedSpread(analytic, quote.getCoupon(), yieldCurve, ((PointsUpFront) quote).getPointsUpFront()); } else if (quote instanceof QuotedSpread) { return (QuotedSpread) quote; } else if (quote instanceof ParSpread) { quotedSpread = POINTS_UP_FRONT_CONVERTER.parSpreadsToQuotedSpreads(new CDSAnalytic[] { analytic }, premium * 1e-4, yieldCurve, new double[] { quote.getCoupon() })[0]; } else { throw new OpenGammaRuntimeException("Unknown quote type " + quote); } // SELL protection reverses directions of legs quotedSpread = Double.valueOf(buySellProtection == BuySellProtection.SELL ? -quotedSpread : quotedSpread); return new QuotedSpread(quote.getCoupon(), quotedSpread); } public static ManageableRegion getTestRegion() { final ManageableRegion region = new ManageableRegion(); region.setUniqueId(UniqueId.parse("Dummy~region")); region.setName("United States"); region.setCurrency(Currency.USD); region.setCountry(Country.US); region.setTimeZone(ZoneId.of("America/New_York")); region.setExternalIdBundle(ExternalIdBundle.of(ExternalId.parse("dummy~region"))); return region; } //@Override //public boolean canHandleMissingRequirements() { // // time series may not be available // return true; //} // //@Override //public boolean canHandleMissingInputs() { // return true; //} @Override public void init(final FunctionCompilationContext context) { // using hardcoded region and calendar for now _holidaySource = new WeekendHolidaySource(); //OpenGammaCompilationContext.getHolidaySource(context); _regionSource = new TestRegionSource(); //OpenGammaCompilationContext.getRegionSource(context); } @Override public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) throws AsynchronousExecution { ValueRequirement requirement = desiredValues.iterator().next(); final Clock snapshotClock = executionContext.getValuationClock(); final ZonedDateTime now = ZonedDateTime.now(snapshotClock); final LegacyVanillaCDSSecurity security = (LegacyVanillaCDSSecurity) target.getSecurity(); 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; CreditDefaultSwapSecurityConverterDeprecated converter = new CreditDefaultSwapSecurityConverterDeprecated( _holidaySource, _regionSource, recoveryRate); LegacyVanillaCreditDefaultSwapDefinition cds = converter.visitLegacyVanillaCDSSecurity(security); final StandardCDSQuotingConvention quoteConvention = StandardCDSQuotingConvention .parse(requirement.getConstraint(ISDAFunctionConstants.CDS_QUOTE_CONVENTION)); final NodalTenorDoubleCurve spreadCurve = (NodalTenorDoubleCurve) inputs .getValue(ValueRequirementNames.BUCKETED_SPREADS); if (spreadCurve == null) { throw new OpenGammaRuntimeException( "Bucketed spreads not available for " + getSpreadCurveIdentifier(security)); } // get the isda curve final ISDACompliantYieldCurve yieldCurve = (ISDACompliantYieldCurve) inputs .getValue(ValueRequirementNames.YIELD_CURVE); if (yieldCurve == null) { throw new OpenGammaRuntimeException("Couldn't get isda curve"); } final Double cdsQuoteDouble = (Double) inputs.getValue(MarketDataRequirementNames.MARKET_VALUE); if (cdsQuoteDouble == null) { throw new OpenGammaRuntimeException("Couldn't get spread for " + security); } final CDSAnalyticVisitor pricingVisitor = new CDSAnalyticVisitor(now.toLocalDate(), _holidaySource, _regionSource, recoveryRate); final CDSAnalytic pricingCDS = security.accept(pricingVisitor); final CDSQuoteConvention quote = SpreadCurveFunctions.getQuotes(security.getMaturityDate(), new double[] { cdsQuoteDouble }, security.getParSpread(), quoteConvention, true)[0]; final QuotedSpread quotedSpread = getQuotedSpread(quote, security.isBuy() ? BuySellProtection.BUY : BuySellProtection.SELL, yieldCurve, pricingCDS, security.getParSpread()); ISDACompliantCreditCurve creditCurve; NodalTenorDoubleCurve modifiedSpreadCurve; NodalTenorDoubleCurve modifiedPillarCurve; if (IMMDateGenerator.isIMMDate(security.getMaturityDate())) { final String pillarString = requirement.getConstraint(ISDAFunctionConstants.ISDA_BUCKET_TENORS); final ZonedDateTime[] bucketDates = SpreadCurveFunctions.getPillarDates(now, pillarString); final ZonedDateTime[] pillarDates = bucketDates; double[] spreads = SpreadCurveFunctions.getSpreadCurveNew(spreadCurve, bucketDates, security.getStartDate(), quoteConvention); Tenor[] tenors = SpreadCurveFunctions.getBuckets(pillarString); modifiedSpreadCurve = new NodalTenorDoubleCurve(tenors, ArrayUtils.toObject(spreads), true); modifiedPillarCurve = modifiedSpreadCurve; // for IMM buckets and spreads are the same //final CDSQuoteConvention[] quotes = SpreadCurveFunctions.getQuotes(security.getMaturityDate(), spreads, security.getParSpread(), quoteConvention, false); // CDS analytics for credit curve final CDSAnalytic[] creditAnalytics = new CDSAnalytic[pillarDates.length]; for (int i = 0; i < creditAnalytics.length; i++) { final CDSAnalyticVisitor curveVisitor = new CDSAnalyticVisitor(now.toLocalDate(), _holidaySource, _regionSource, security.getStartDate().toLocalDate(), pillarDates[i].toLocalDate(), recoveryRate); creditAnalytics[i] = security.accept(curveVisitor); } creditCurve = CREDIT_CURVE_BUILDER.calibrateCreditCurve(pricingCDS, quotedSpread, yieldCurve); } else { // non IMM date - pillars set to fixed set final String pillarString = NON_IMM_PILLAR_TENORS; final String bucketString = requirement.getConstraint(ISDAFunctionConstants.ISDA_BUCKET_TENORS); final ZonedDateTime[] bucketDates = SpreadCurveFunctions.getPillarDates(now, bucketString); final ZonedDateTime[] pillarDates = SpreadCurveFunctions.getPillarDates(now, pillarString); double[] bucketSpreads = SpreadCurveFunctions.getSpreadCurveNew(spreadCurve, bucketDates, security.getStartDate(), quoteConvention); double[] pillarSpreads = SpreadCurveFunctions.getSpreadCurveNew(spreadCurve, pillarDates, security.getStartDate(), quoteConvention); Tenor[] bucketTenors = SpreadCurveFunctions.getBuckets(bucketString); Tenor[] pillarTenors = SpreadCurveFunctions.getBuckets(pillarString); modifiedSpreadCurve = new NodalTenorDoubleCurve(bucketTenors, ArrayUtils.toObject(bucketSpreads), true); modifiedPillarCurve = new NodalTenorDoubleCurve(pillarTenors, ArrayUtils.toObject(pillarSpreads), true); final CDSQuoteConvention[] quotes = SpreadCurveFunctions.getQuotes(security.getMaturityDate(), pillarSpreads, security.getParSpread(), quoteConvention, false); // CDS analytics for credit curve final CDSAnalytic[] creditAnalytics = new CDSAnalytic[pillarDates.length]; for (int i = 0; i < creditAnalytics.length; i++) { final CDSAnalyticVisitor curveVisitor = new CDSAnalyticVisitor(now.toLocalDate(), _holidaySource, _regionSource, security.getStartDate().toLocalDate(), pillarDates[i].toLocalDate(), recoveryRate); creditAnalytics[i] = security.accept(curveVisitor); } creditCurve = CREDIT_CURVE_BUILDER.calibrateCreditCurve(creditAnalytics, quotes, yieldCurve); } //if (IMMDateGenerator.isIMMDate(security.getMaturityDate())) { // creditCurve = CREDIT_CURVE_BUILDER.calibrateCreditCurve(pricingCDS, quotedSpread, yieldCurve); //} else { // creditCurve = CREDIT_CURVE_BUILDER.calibrateCreditCurve(creditAnalytics, quotes, yieldCurve); //} //if (IMMDateGenerator.isIMMDate(security.getMaturityDate())) { // // form from single point instead of all // final int index = Arrays.binarySearch(spreadCurve, security.getMaturityDate()); // ArgumentChecker.isTrue(index > 0, "cds maturity " + security + " not in pillar dates"); // creditCurve = creditCurveBuilder.calibrateCreditCurve(new CDSAnalytic[] { creditAnalytics[index] }, // new CDSQuoteConvention[] { quotes[index] }, yieldCurve); //} else { // creditCurve = creditCurveBuilder.calibrateCreditCurve(creditAnalytics, quotes, yieldCurve); //} final ValueSpecification spec = new ValueSpecification(ValueRequirementNames.HAZARD_RATE_CURVE, target.toSpecification(), requirement.getConstraints()); // spreads final ValueSpecification spreadSpec = new ValueSpecification(ValueRequirementNames.BUCKETED_SPREADS, target.toSpecification(), requirement.getConstraints()); final ValueSpecification pillarSpec = new ValueSpecification(ValueRequirementNames.PILLAR_SPREADS, target.toSpecification(), requirement.getConstraints()); return Sets.newHashSet(new ComputedValue(spec, creditCurve), new ComputedValue(spreadSpec, modifiedSpreadCurve), new ComputedValue(pillarSpec, modifiedPillarCurve)); } @Override public ComputationTargetType getTargetType() { return FinancialSecurityTypes.LEGACY_VANILLA_CDS_SECURITY; } @Override public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) { @SuppressWarnings("synthetic-access") final ValueProperties properties = createValueProperties() .withAny(ISDAFunctionConstants.CDS_QUOTE_CONVENTION) .withAny(ISDAFunctionConstants.ISDA_BUCKET_TENORS).withAny(ISDAFunctionConstants.ISDA_CURVE_OFFSET) .withAny(ISDAFunctionConstants.ISDA_CURVE_DATE) .with(ISDAFunctionConstants.ISDA_IMPLEMENTATION, ISDAFunctionConstants.ISDA_IMPLEMENTATION_NEW) .with(ValuePropertyNames.CURVE_CALCULATION_METHOD, ISDAFunctionConstants.ISDA_METHOD_NAME).get(); final ValueSpecification creditCurveSpec = new ValueSpecification(ValueRequirementNames.HAZARD_RATE_CURVE, target.toSpecification(), properties); final ValueProperties spreadProperties = createValueProperties() .withAny(ISDAFunctionConstants.CDS_QUOTE_CONVENTION) .withAny(ISDAFunctionConstants.ISDA_BUCKET_TENORS).withAny(ISDAFunctionConstants.ISDA_CURVE_OFFSET) .withAny(ISDAFunctionConstants.ISDA_CURVE_DATE) .with(ISDAFunctionConstants.ISDA_IMPLEMENTATION, ISDAFunctionConstants.ISDA_IMPLEMENTATION_NEW) .with(ValuePropertyNames.CURVE_CALCULATION_METHOD, ISDAFunctionConstants.ISDA_METHOD_NAME).get(); final ValueSpecification spreadSpec = new ValueSpecification(ValueRequirementNames.BUCKETED_SPREADS, target.toSpecification(), spreadProperties); final ValueSpecification pillarSpec = new ValueSpecification(ValueRequirementNames.PILLAR_SPREADS, target.toSpecification(), spreadProperties); return Sets.newHashSet(creditCurveSpec, spreadSpec, pillarSpec); } @Override public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) { final CreditDefaultSwapSecurity cds = (CreditDefaultSwapSecurity) target.getSecurity(); final CreditCurveIdentifier spreadIdentifier = getSpreadCurveIdentifier(cds); final Currency ccy = cds.getNotional().getCurrency(); final CreditCurveIdentifier isdaIdentifier = getISDACurveIdentifier(cds); final String isdaOffset = desiredValue.getConstraint(ISDAFunctionConstants.ISDA_CURVE_OFFSET); if (isdaOffset == null) { return null; } final String isdaCurveDate = desiredValue.getConstraint(ISDAFunctionConstants.ISDA_CURVE_DATE); if (isdaCurveDate == null) { return null; } final String isdaCurveMethod = desiredValue.getConstraint(ISDAFunctionConstants.ISDA_IMPLEMENTATION); if (isdaCurveMethod == null) { return null; } if (desiredValue.getConstraint(ISDAFunctionConstants.CDS_QUOTE_CONVENTION) == null) { return null; } // isda curve final ValueProperties isdaProperties = ValueProperties.builder() .with(ValuePropertyNames.CURVE, isdaIdentifier.toString()) .with(ValuePropertyNames.CURVE_CALCULATION_METHOD, ISDAFunctionConstants.ISDA_METHOD_NAME) .with(ISDAFunctionConstants.ISDA_CURVE_OFFSET, isdaOffset) .with(ISDAFunctionConstants.ISDA_CURVE_DATE, isdaCurveDate) .with(ISDAFunctionConstants.ISDA_IMPLEMENTATION, isdaCurveMethod).get(); final ValueRequirement isdaRequirment = new ValueRequirement(ValueRequirementNames.YIELD_CURVE, ComputationTargetType.CURRENCY, ccy.getUniqueId(), isdaProperties); final ValueRequirement spreadRequirment = new ValueRequirement(ValueRequirementNames.BUCKETED_SPREADS, ComputationTargetType.PRIMITIVE, spreadIdentifier.getUniqueId()); // get individual spread for this cds (ignore business day adjustment on either) final Period period = Period.between(cds.getStartDate().toLocalDate().withDayOfMonth(20), cds.getMaturityDate().toLocalDate().withDayOfMonth(20)); final ValueRequirement cdsSpreadRequirement = new ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, ComputationTargetType.PRIMITIVE, ExternalId.of("Tenor", period.toString())); final CdsRecoveryRateIdentifier recoveryRateIdentifier = cds .accept(new CreditSecurityToRecoveryRateVisitor(context.getSecuritySource())); final ValueRequirement recoveryRateRequirement = new ValueRequirement("PX_LAST", ComputationTargetType.PRIMITIVE, recoveryRateIdentifier.getExternalId()); return Sets.newHashSet(spreadRequirment, isdaRequirment, cdsSpreadRequirement, recoveryRateRequirement); } public class TestRegionSource extends AbstractSourceWithExternalBundle<Region> implements RegionSource { private final AtomicLong _count = new AtomicLong(0); private final Region _testRegion; private TestRegionSource(final Region testRegion) { _testRegion = testRegion; } private TestRegionSource() { _testRegion = getTestRegion(); } @Override public Collection<Region> get(final ExternalIdBundle bundle, final VersionCorrection versionCorrection) { _count.getAndIncrement(); Collection<Region> result = Collections.emptyList(); if (_testRegion.getExternalIdBundle().equals(bundle) && versionCorrection.equals(VersionCorrection.LATEST)) { result = Collections.singleton((Region) getTestRegion()); } return result; } @Override public Region get(final ObjectId objectId, final VersionCorrection versionCorrection) { _count.getAndIncrement(); Region result = null; if (_testRegion.getUniqueId().getObjectId().equals(objectId) && versionCorrection.equals(VersionCorrection.LATEST)) { result = _testRegion; } return result; } @Override public Region get(final UniqueId uniqueId) { _count.getAndIncrement(); Region result = null; if (_testRegion.getUniqueId().equals(uniqueId)) { result = _testRegion; } return result; } @Override public Region getHighestLevelRegion(final ExternalIdBundle bundle) { _count.getAndIncrement(); Region result = null; if (_testRegion.getExternalIdBundle().equals(bundle)) { result = _testRegion; } return result; } @Override public Region getHighestLevelRegion(final ExternalId externalId) { _count.getAndIncrement(); Region result = null; if (_testRegion.getExternalIdBundle().contains(externalId)) { result = _testRegion; } return result; } /** * Gets the count. * * @return the count */ public AtomicLong getCount() { return _count; } @Override public ChangeManager changeManager() { return DummyChangeManager.INSTANCE; } } };