/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.analytics.curve; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.joda.beans.impl.flexi.FlexiBean; import org.threeten.bp.Period; import org.threeten.bp.ZoneId; import org.threeten.bp.ZonedDateTime; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.analytics.financial.instrument.InstrumentDefinition; import com.opengamma.analytics.financial.instrument.bond.BondFixedSecurityDefinition; import com.opengamma.analytics.financial.instrument.bond.BondFixedTransactionDefinition; import com.opengamma.analytics.financial.instrument.payment.PaymentFixedDefinition; import com.opengamma.analytics.financial.legalentity.CreditRating; import com.opengamma.analytics.financial.legalentity.LegalEntity; import com.opengamma.analytics.financial.legalentity.Region; import com.opengamma.analytics.financial.legalentity.Sector; import com.opengamma.analytics.financial.schedule.ScheduleCalculator; import com.opengamma.core.holiday.HolidaySource; import com.opengamma.core.id.ExternalSchemes; import com.opengamma.core.link.SecurityLink; import com.opengamma.core.marketdatasnapshot.SnapshotDataBundle; import com.opengamma.core.region.RegionSource; import com.opengamma.core.security.SecuritySource; import com.opengamma.financial.analytics.conversion.CalendarUtils; import com.opengamma.financial.analytics.conversion.ConversionUtils; import com.opengamma.financial.analytics.conversion.LegalEntityUtils; import com.opengamma.financial.analytics.ircurve.strips.BondNode; import com.opengamma.financial.convention.ConventionBundle; import com.opengamma.financial.convention.ConventionBundleSource; import com.opengamma.financial.convention.InMemoryConventionBundleMaster; import com.opengamma.financial.convention.businessday.BusinessDayConvention; import com.opengamma.financial.convention.businessday.BusinessDayConventions; import com.opengamma.financial.convention.calendar.Calendar; import com.opengamma.financial.convention.daycount.DayCount; import com.opengamma.financial.convention.yield.YieldConvention; import com.opengamma.financial.security.bond.BondSecurity; import com.opengamma.id.ExternalId; import com.opengamma.id.ExternalIdBundle; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.i18n.Country; import com.opengamma.util.money.Currency; /** * */ public class BondNodeConverter extends CurveNodeVisitorAdapter<InstrumentDefinition<?>> { /** The region source */ private final RegionSource _regionSource; /** The holiday source */ private final HolidaySource _holidaySource; /** The convention source */ private final ConventionBundleSource _conventionSource; /** The market data */ private final SnapshotDataBundle _marketData; /** The market data id */ private final ExternalId _dataId; /** The valuation time */ private final ZonedDateTime _valuationTime; /** * @param regionSource The region source, not null * @param holidaySource The holiday source, not null * @param conventionSource The convention source, not null * @param marketData The market data, not null * @param dataId The market data id, not null * @param valuationTime The valuation time, not null */ //TODO PLAT-6801 replace conventionBundleSource with conventionSource public BondNodeConverter( RegionSource regionSource, HolidaySource holidaySource, ConventionBundleSource conventionSource, SnapshotDataBundle marketData, ExternalId dataId, ZonedDateTime valuationTime) { _regionSource = ArgumentChecker.notNull(regionSource, "regionSource"); _holidaySource = ArgumentChecker.notNull(holidaySource, "holidaySource"); _conventionSource = ArgumentChecker.notNull(conventionSource, "conventionSource"); _marketData = ArgumentChecker.notNull(marketData, "marketData"); _dataId = ArgumentChecker.notNull(dataId, "dataId"); _valuationTime = ArgumentChecker.notNull(valuationTime, "valuationTime"); } /** * @param regionSource The region source, not null * @param holidaySource The holiday source, not null * @param conventionSource The convention source, not null * @param securitySource The security source, not null * @param marketData The market data, not null * @param dataId The market data id, not null * @param valuationTime The valuation time, not null * @deprecated use constructor without securitySource */ @Deprecated public BondNodeConverter(ConventionBundleSource conventionSource, HolidaySource holidaySource, RegionSource regionSource, SecuritySource securitySource, SnapshotDataBundle marketData, ExternalId dataId, ZonedDateTime valuationTime) { this(regionSource, holidaySource, conventionSource, marketData, dataId, valuationTime); } @Override public InstrumentDefinition<?> visitBondNode(BondNode bondNode) { Double yield = _marketData.getDataPoint(_dataId); if (yield == null) { throw new OpenGammaRuntimeException("Could not get market data for " + _dataId); } // TODO this is in here because we can't ask for data by ISIN directly. BondSecurity bondSecurity = SecurityLink.resolvable(_dataId, BondSecurity.class).resolve(); ExternalId regionId = ExternalSchemes.financialRegionId(bondSecurity.getIssuerDomicile()); if (regionId == null) { throw new OpenGammaRuntimeException("Could not find region for " + bondSecurity.getIssuerDomicile()); } Calendar calendar = CalendarUtils.getCalendar(_regionSource, _holidaySource, regionId); Currency currency = bondSecurity.getCurrency(); ZoneId zone = bondSecurity.getInterestAccrualDate().getZone(); ZonedDateTime firstAccrualDate = ZonedDateTime.of(bondSecurity.getInterestAccrualDate().toLocalDate().atStartOfDay(), zone); ZonedDateTime maturityDate = ZonedDateTime.of(bondSecurity.getLastTradeDate().getExpiry().toLocalDate().atStartOfDay(), zone); double rate = bondSecurity.getCouponRate() / 100; DayCount dayCount = bondSecurity.getDayCount(); BusinessDayConvention businessDay = BusinessDayConventions.FOLLOWING; String domicile = bondSecurity.getIssuerDomicile(); if (domicile == null) { throw new OpenGammaRuntimeException("bond security domicile cannot be null"); } String conventionName = domicile + "_TREASURY_BOND_CONVENTION"; ConventionBundle convention = _conventionSource.getConventionBundle( ExternalId.of(InMemoryConventionBundleMaster.SIMPLE_NAME_SCHEME, conventionName)); if (convention == null) { throw new OpenGammaRuntimeException("Convention called " + conventionName + " was null"); } if (convention.isEOMConvention() == null) { throw new OpenGammaRuntimeException("Could not get EOM convention information from " + conventionName); } boolean isEOM = convention.isEOMConvention(); YieldConvention yieldConvention = bondSecurity.getYieldConvention(); if (bondSecurity.getCouponType().equals("NONE") || bondSecurity.getCouponType().equals("ZERO COUPON")) { //TODO find where string is return new PaymentFixedDefinition(currency, maturityDate, 1); } if (convention.getBondSettlementDays(firstAccrualDate, maturityDate) == null) { throw new OpenGammaRuntimeException("Could not get bond settlement days from " + conventionName); } int settlementDays = convention.getBondSettlementDays(firstAccrualDate, maturityDate); int exCouponDays = convention.getExDividendDays(); Period paymentPeriod = ConversionUtils.getTenor(bondSecurity.getCouponFrequency()); ZonedDateTime firstCouponDate = ZonedDateTime.of(bondSecurity.getFirstCouponDate().toLocalDate().atStartOfDay(), zone); ExternalIdBundle identifiers = bondSecurity.getExternalIdBundle(); String isin = identifiers.getValue(ExternalSchemes.ISIN); String ticker = isin == null ? null : isin; String shortName = bondSecurity.getIssuerName(); String sectorName = bondSecurity.getIssuerType(); FlexiBean classifications = new FlexiBean(); classifications.put(LegalEntityUtils.MARKET_STRING, bondSecurity.getMarket()); Sector sector = Sector.of(sectorName, classifications); Region region = Region.of( bondSecurity.getIssuerDomicile(), Country.of(bondSecurity.getIssuerDomicile()), bondSecurity.getCurrency()); Map<String, String> securityAttributes = bondSecurity.getAttributes(); Set<CreditRating> creditRatings = null; for (String ratingString : LegalEntityUtils.RATING_STRINGS) { if (securityAttributes.containsKey(ratingString)) { if (creditRatings == null) { creditRatings = new HashSet<>(); } creditRatings.add(CreditRating.of(securityAttributes.get(ratingString), ratingString, true)); } } LegalEntity legalEntity = new LegalEntity(ticker, shortName, creditRatings, sector, region); BondFixedSecurityDefinition securityDefinition = BondFixedSecurityDefinition.from(currency, firstAccrualDate, firstCouponDate, maturityDate, paymentPeriod, rate, settlementDays, exCouponDays, calendar, dayCount, businessDay, yieldConvention, isEOM, legalEntity); // TODO: PLAT-5253 Standard days to settlement are missing in bond description. ZonedDateTime settleDate = ScheduleCalculator.getAdjustedDate(_valuationTime, settlementDays, calendar); return BondFixedTransactionDefinition.fromYield(securityDefinition, 1, settleDate, yield); // TODO: User should choose between yield and price. } }