/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.analytics.conversion;
import java.util.Collections;
import org.threeten.bp.LocalDate;
import org.threeten.bp.LocalTime;
import org.threeten.bp.Period;
import org.threeten.bp.ZoneId;
import org.threeten.bp.ZoneOffset;
import org.threeten.bp.ZonedDateTime;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.analytics.financial.instrument.InstrumentDefinition;
import com.opengamma.analytics.financial.instrument.annuity.AnnuityDefinition;
import com.opengamma.analytics.financial.instrument.bond.BondFixedSecurityDefinition;
import com.opengamma.analytics.financial.instrument.bond.BondTotalReturnSwapDefinition;
import com.opengamma.analytics.financial.instrument.payment.PaymentDefinition;
import com.opengamma.analytics.financial.legalentity.LegalEntity;
import com.opengamma.core.convention.ConventionSource;
import com.opengamma.core.holiday.HolidaySource;
import com.opengamma.core.id.ExternalSchemes;
import com.opengamma.core.region.RegionSource;
import com.opengamma.core.security.SecuritySource;
import com.opengamma.financial.convention.BondConvention;
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.FinancialSecurity;
import com.opengamma.financial.security.FinancialSecurityVisitorAdapter;
import com.opengamma.financial.security.bond.BondSecurity;
import com.opengamma.financial.security.bond.CorporateBondSecurity;
import com.opengamma.financial.security.bond.GovernmentBondSecurity;
import com.opengamma.financial.security.irs.FloatingInterestRateSwapLeg;
import com.opengamma.financial.security.irs.NotionalExchange;
import com.opengamma.financial.security.irs.PayReceiveType;
import com.opengamma.financial.security.swap.BondTotalReturnSwapSecurity;
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;
/**
* Converts {@link BondTotalReturnSwapSecurity} classes to {@link BondTotalReturnSwapDefinition},
* which are required for use in the analytics library.
* The asset leg notional amount is used as bond quantity and the underlying bond has a notional of 1.0.
* The bond TRS notional currency is not used, the bond currency is used in the bond description.
*/
public class BondTotalReturnSwapSecurityConverter extends FinancialSecurityVisitorAdapter<InstrumentDefinition<?>> {
/** The convention source */
private final ConventionSource _conventionSource;
/** The holiday source */
private final HolidaySource _holidaySource;
/** The region source */
private final RegionSource _regionSource;
/** The security source */
private final SecuritySource _securitySource;
/**
* @param conventionSource The convention source, not null
* @param holidaySource The holiday source, not null
* @param regionSource The region source, not null
* @param securitySource The security source, not null
*/
public BondTotalReturnSwapSecurityConverter(final ConventionSource conventionSource, final HolidaySource holidaySource,
final RegionSource regionSource, final SecuritySource securitySource) {
ArgumentChecker.notNull(conventionSource, "conventionSource");
ArgumentChecker.notNull(holidaySource, "holidaySource");
ArgumentChecker.notNull(regionSource, "regionSource");
ArgumentChecker.notNull(securitySource, "securitySource");
_conventionSource = conventionSource;
_holidaySource = holidaySource;
_regionSource = regionSource;
_securitySource = securitySource;
}
@Override
public BondTotalReturnSwapDefinition visitBondTotalReturnSwapSecurity(final BondTotalReturnSwapSecurity security) {
ArgumentChecker.notNull(security, "security");
final FinancialSecurity underlying = (FinancialSecurity) _securitySource.getSingle(security.getAssetId().toBundle()); //TODO ignoring version
if (!(underlying instanceof BondSecurity)) {
throw new OpenGammaRuntimeException("Underlying for bond TRS was not a bond");
}
final FloatingInterestRateSwapLeg fundingLeg = security.getFundingLeg();
final boolean isPayer = fundingLeg.getPayReceiveType() == PayReceiveType.PAY ? true : false;
final LocalDate startDate = security.getEffectiveDate();
final LocalDate endDate = security.getMaturityDate();
final NotionalExchange notionalExchange = NotionalExchange.builder().exchangeFinalNotional(true).build(); //NotionalExchange.NO_EXCHANGE;
final AnnuityDefinition<? extends PaymentDefinition> annuityDefinition =
AnnuityUtils.buildFloatingAnnuityDefinition(_conventionSource, _holidaySource, _securitySource, isPayer,
startDate, endDate, notionalExchange, fundingLeg);
final BondSecurity bond = (BondSecurity) underlying;
final BondConvention convention = getConvention(bond, _conventionSource);
final LegalEntity legalEntity = LegalEntityUtils.getLegalEntityForBond(Collections.<String, String>emptyMap(), bond);
final ExternalId regionId = ExternalSchemes.financialRegionId(bond.getIssuerDomicile());
if (regionId == null) {
throw new OpenGammaRuntimeException("Could not find region for " + bond.getIssuerDomicile());
}
final Currency currency = bond.getCurrency();
final Calendar calendar;
// If the bond is Supranational, we use the calendar derived from the currency of the bond.
// this may need revisiting.
if (regionId.getValue().equals("SNAT")) { // Supranational
calendar = CalendarUtils.getCalendar(_holidaySource, currency);
} else {
calendar = CalendarUtils.getCalendar(_regionSource, _holidaySource, regionId);
}
if (bond.getInterestAccrualDate() == null) {
throw new OpenGammaRuntimeException("Bond first interest accrual date was null");
}
final ZoneId zone = bond.getInterestAccrualDate().getZone();
final ZonedDateTime firstAccrualDate = ZonedDateTime.of(bond.getInterestAccrualDate().toLocalDate().atStartOfDay(), zone);
final ZonedDateTime maturityDate = ZonedDateTime.of(bond.getLastTradeDate().getExpiry().toLocalDate().atStartOfDay(), zone);
final double rate = bond.getCouponRate() / 100;
final DayCount dayCount = bond.getDayCount();
final BusinessDayConvention businessDay = BusinessDayConventions.FOLLOWING;
final boolean isEOM = convention.isIsEOM();
final YieldConvention yieldConvention = bond.getYieldConvention();
final int settlementDays = convention.getSettlementDays();
final Period paymentPeriod = ConversionUtils.getTenor(bond.getCouponFrequency());
final ZonedDateTime firstCouponDate = ZonedDateTime.of(bond.getFirstCouponDate().toLocalDate().atStartOfDay(), zone);
final double notional = security.getNotionalAmount();
final int exDividendDays = 0;
final BondFixedSecurityDefinition bondDefinition = BondFixedSecurityDefinition.from(currency, firstAccrualDate, firstCouponDate,
maturityDate, paymentPeriod, rate, settlementDays, 1.0d, exDividendDays, calendar, dayCount, businessDay,
yieldConvention, isEOM, legalEntity);
final ZonedDateTime startDateTime = startDate.atTime(LocalTime.MIN).atZone(ZoneOffset.UTC);
final ZonedDateTime endDateTime = endDate.atTime(LocalTime.MIN).atZone(ZoneOffset.UTC);
return new BondTotalReturnSwapDefinition(startDateTime, endDateTime, annuityDefinition, bondDefinition, notional);
}
/**
* Gets the convention for a bond from its domicile.
* @param bond The bond
* @return The convention
*/
private static BondConvention getConvention(final BondSecurity bond, final ConventionSource conventionSource) {
return bond.accept(new FinancialSecurityVisitorAdapter<BondConvention>() {
@Override
public BondConvention visitCorporateBondSecurity(final CorporateBondSecurity corporateBond) {
final String domicile = corporateBond.getIssuerDomicile();
final ExternalId countryId = ExternalSchemes.countryRegionId(Country.of(domicile));
final ExternalId currencyId = ExternalSchemes.currencyRegionId(corporateBond.getCurrency());
final ExternalIdBundle conventionId = ExternalIdBundle.of(countryId, currencyId);
final BondConvention convention = conventionSource.getSingle(conventionId, BondConvention.class);
if (convention == null) {
throw new OpenGammaRuntimeException("No bond convention found for domicile " + domicile);
}
return convention;
}
@Override
public BondConvention visitGovernmentBondSecurity(final GovernmentBondSecurity governmentBond) {
final String domicile = governmentBond.getIssuerDomicile();
final ExternalId countryId = ExternalSchemes.countryRegionId(Country.of(domicile));
final ExternalId currencyId = ExternalSchemes.currencyRegionId(governmentBond.getCurrency());
final ExternalIdBundle conventionId = ExternalIdBundle.of(countryId, currencyId);
final BondConvention convention = conventionSource.getSingle(conventionId, BondConvention.class);
if (convention == null) {
throw new OpenGammaRuntimeException("No bond convention found for domicile " + domicile);
}
return convention;
}
});
}
}