/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.analytics.model; import static com.opengamma.engine.value.ValueRequirementNames.HISTORICAL_TIME_SERIES; import java.util.Collections; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.threeten.bp.LocalDate; 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.BondCapitalIndexedTransactionDefinition; import com.opengamma.analytics.financial.instrument.future.BondFuturesTransactionDefinition; import com.opengamma.analytics.financial.interestrate.InstrumentDerivative; import com.opengamma.core.convention.ConventionSource; import com.opengamma.core.historicaltimeseries.HistoricalTimeSeries; import com.opengamma.core.holiday.HolidaySource; import com.opengamma.core.legalentity.LegalEntitySource; import com.opengamma.core.position.Trade; import com.opengamma.core.region.RegionSource; import com.opengamma.core.security.Security; import com.opengamma.core.security.SecuritySource; import com.opengamma.core.value.MarketDataRequirementNames; import com.opengamma.engine.ComputationTarget; import com.opengamma.engine.function.FunctionExecutionContext; import com.opengamma.engine.function.FunctionInputs; import com.opengamma.engine.target.ComputationTargetType; import com.opengamma.engine.value.ValueRequirement; import com.opengamma.financial.OpenGammaExecutionContext; import com.opengamma.financial.analytics.conversion.BondAndBondFutureTradeConverter; import com.opengamma.financial.analytics.timeseries.DateConstraint; import com.opengamma.financial.analytics.timeseries.HistoricalTimeSeriesFunctionUtils; import com.opengamma.financial.convention.ConventionBundleSource; import com.opengamma.financial.security.FinancialSecurity; import com.opengamma.financial.security.bond.BillSecurity; import com.opengamma.financial.security.bond.BondSecurity; import com.opengamma.financial.security.bond.InflationBondSecurity; import com.opengamma.financial.security.future.BondFutureSecurity; import com.opengamma.id.ExternalId; import com.opengamma.id.ExternalIdBundle; import com.opengamma.master.historicaltimeseries.HistoricalTimeSeriesResolutionResult; import com.opengamma.master.historicaltimeseries.HistoricalTimeSeriesResolver; import com.opengamma.timeseries.date.localdate.LocalDateDoubleEntryIterator; import com.opengamma.timeseries.date.localdate.LocalDateDoubleTimeSeries; import com.opengamma.timeseries.precise.zdt.ImmutableZonedDateTimeDoubleTimeSeries; import com.opengamma.timeseries.precise.zdt.ZonedDateTimeDoubleTimeSeries; import com.opengamma.timeseries.precise.zdt.ZonedDateTimeDoubleTimeSeriesBuilder; import com.opengamma.util.ArgumentChecker; /** * Utility methods that are used in functions that calculate bond analytics. */ public class BondAndBondFutureFunctionUtils { /** The logger */ private static final Logger s_logger = LoggerFactory.getLogger(BondAndBondFutureFunctionUtils.class); /** * Gets any additional value requirements that are required for conversion from securities * to analytics objects. For bond futures, the future price time series is required. For all * other securities, an empty set is returned * @param security The security, not null * @param timeSeriesResolver The time series resolver, not null if the security is a bond future * @return The set of requirements */ public static Set<ValueRequirement> getConversionRequirements(final FinancialSecurity security, final HistoricalTimeSeriesResolver timeSeriesResolver) { ArgumentChecker.notNull(security, "security"); if (security instanceof BondFutureSecurity) { ArgumentChecker.notNull(timeSeriesResolver, "timeSeriesResolver"); final ExternalIdBundle externalIdBundle = security.getExternalIdBundle(); final HistoricalTimeSeriesResolutionResult timeSeries = timeSeriesResolver.resolve(externalIdBundle, null, null, null, MarketDataRequirementNames.MARKET_VALUE, null); if (timeSeries == null) { s_logger.error("Could not resolve time series for {}", externalIdBundle); return Collections.emptySet(); } return Collections.singleton(HistoricalTimeSeriesFunctionUtils.createHTSRequirement(timeSeries, MarketDataRequirementNames.MARKET_VALUE, DateConstraint.VALUATION_TIME.minus(Period.ofMonths(1)).previousWeekDay(), true, DateConstraint.VALUATION_TIME, true)); } if (security instanceof InflationBondSecurity) { ArgumentChecker.notNull(timeSeriesResolver, "timeSeriesResolver"); final ExternalIdBundle externalIdBundle = ExternalIdBundle.of(ExternalId.parse(((InflationBondSecurity) security).attributes().get().get("ReferenceIndexId"))); final HistoricalTimeSeriesResolutionResult timeSeries = timeSeriesResolver.resolve(externalIdBundle, null, null, null, MarketDataRequirementNames.MARKET_VALUE, null); if (timeSeries == null) { s_logger.error("Could not resolve time series for {}", externalIdBundle); return Collections.emptySet(); } return Collections.singleton(HistoricalTimeSeriesFunctionUtils.createHTSRequirement(timeSeries, MarketDataRequirementNames.MARKET_VALUE, DateConstraint.VALUATION_TIME.minus(Period.ofMonths(12)), true, DateConstraint.VALUATION_TIME, true)); } return Collections.emptySet(); } /** * Converts a bond or bond future trade into the {@link InstrumentDefinition} form that is used in * the analytics library. * @param context The execution context, not null * @param target The computation target, not null * @param date The valuation date / time, not null * @return The definition form of a bond or bond future security */ public static InstrumentDefinition<?> getDefinition(final FunctionExecutionContext context, final ComputationTarget target, final ZonedDateTime date) { ArgumentChecker.notNull(context, "context"); ArgumentChecker.notNull(target, "target"); ArgumentChecker.notNull(date, "date"); ArgumentChecker.isTrue(target.getType() == ComputationTargetType.TRADE, "Computation target must be a trade"); final Trade trade = target.getTrade(); final HolidaySource holidaySource = OpenGammaExecutionContext.getHolidaySource(context); final ConventionBundleSource conventionBundleSource = OpenGammaExecutionContext.getConventionBundleSource(context); final ConventionSource conventionSource = OpenGammaExecutionContext.getConventionSource(context); final RegionSource regionSource = OpenGammaExecutionContext.getRegionSource(context); final SecuritySource securitySource = OpenGammaExecutionContext.getSecuritySource(context); final LegalEntitySource legalEntitySource = OpenGammaExecutionContext.getLegalEntitySource(context); final BondAndBondFutureTradeConverter converter = new BondAndBondFutureTradeConverter(holidaySource, conventionBundleSource, conventionSource, regionSource, securitySource, legalEntitySource); return converter.convert(trade); } private static ZonedDateTimeDoubleTimeSeries convertTimeSeries(final ZoneId timeZone, final LocalDateDoubleTimeSeries localDateTS) { // FIXME CASE Converting a daily historical time series to an arbitrary time. Bad idea final ZonedDateTimeDoubleTimeSeriesBuilder bld = ImmutableZonedDateTimeDoubleTimeSeries.builder(timeZone); for (final LocalDateDoubleEntryIterator it = localDateTS.iterator(); it.hasNext();) { final LocalDate date = it.nextTime(); final ZonedDateTime zdt = date.atStartOfDay(timeZone); bld.put(zdt, it.currentValueFast()); } return bld.build(); } /** * Converts a bond or bond future trade into the {@link InstrumentDerivative} form that is used in * pricing functions in the analytics library. * @param context The execution context, not null * @param target The computation target, not null * @param date The valuation date / time, not null * @param inputs The function inputs, not null if the security is a {@link BondFutureSecurity} * @return The derivative form of the security */ public static InstrumentDerivative getBondOrBondFutureDerivative(final FunctionExecutionContext context, final ComputationTarget target, final ZonedDateTime date, final FunctionInputs inputs) { ArgumentChecker.notNull(target, "target"); ArgumentChecker.isTrue(target.getType() == ComputationTargetType.TRADE, "Computation target must be a trade"); final Trade trade = target.getTrade(); final Security security = trade.getSecurity(); if (security instanceof BondSecurity & !(security instanceof InflationBondSecurity)) { return getBondDerivative(context, target, date); } if (security instanceof InflationBondSecurity) { final HistoricalTimeSeries indexPriceSeries = (HistoricalTimeSeries) inputs.getValue(HISTORICAL_TIME_SERIES); final LocalDateDoubleTimeSeries ts = indexPriceSeries.getTimeSeries(); final ZonedDateTimeDoubleTimeSeries indexTS = convertTimeSeries(date.getZone(), ts.multiply(100.0)); return getBondInflationDerivative(context, target, date, indexTS); } if (security instanceof BillSecurity) { return getBillDerivative(context, target, date); } if (security instanceof BondFutureSecurity) { ArgumentChecker.notNull(inputs, "inputs"); final HistoricalTimeSeries futurePriceSeries = (HistoricalTimeSeries) inputs.getValue(HISTORICAL_TIME_SERIES); return getBondFutureDerivative(context, target, date, futurePriceSeries); } throw new OpenGammaRuntimeException("Unsupported security type " + security.getClass()); } /** * Converts a bond trade into the {@link InstrumentDerivative} form that is used in pricing * functions in the the analytics library. * @param context The execution context, not null * @param target The computation target, not null * @param date The valuation date / time, not null * @return The derivative form of a bond security */ private static InstrumentDerivative getBondDerivative(final FunctionExecutionContext context, final ComputationTarget target, final ZonedDateTime date) { final InstrumentDefinition<?> definition = getDefinition(context, target, date); return definition.toDerivative(date); } /** * Converts a bond trade into the {@link InstrumentDerivative} form that is used in pricing * functions in the the analytics library. * @param context The execution context, not null * @param target The computation target, not null * @param date The valuation date / time, not null * @return The derivative form of a bond security */ private static InstrumentDerivative getBondInflationDerivative(final FunctionExecutionContext context, final ComputationTarget target, final ZonedDateTime date, final ZonedDateTimeDoubleTimeSeries ts) { final InstrumentDefinition<?> definition = getDefinition(context, target, date); return ((BondCapitalIndexedTransactionDefinition<?>) definition).toDerivative(date, ts); } /** * Converts a bill trade into the {@link InstrumentDerivative} form that is used in pricing * functions in the the analytics library. * @param context The execution context, not null * @param target The computation target, not null * @param date The valuation date / time, not null * @return The derivative form of a bill security */ private static InstrumentDerivative getBillDerivative(final FunctionExecutionContext context, final ComputationTarget target, final ZonedDateTime date) { final InstrumentDefinition<?> definition = getDefinition(context, target, date); return definition.toDerivative(date); } /** * Converts a bond future trade into the {@link InstrumentDerivative} form that is used in pricing * functions in the the analytics library. * @param context The execution context, not null * @param target The computation target, not null * @param date The valuation date / time, not null * @param futurePriceSeries The bond future price time series, not null * @return The derivative form of a bond security * @throws OpenGammaRuntimeException If the bond future price series is empty */ private static InstrumentDerivative getBondFutureDerivative(final FunctionExecutionContext context, final ComputationTarget target, final ZonedDateTime date, final HistoricalTimeSeries futurePriceSeries) { final BondFutureSecurity security = (BondFutureSecurity) target.getTrade().getSecurity(); ArgumentChecker.notNull(futurePriceSeries, "futurePriceSeries"); final InstrumentDefinition<?> definition = getDefinition(context, target, date); final LocalDateDoubleTimeSeries ts = futurePriceSeries.getTimeSeries(); final int length = ts.size(); if (length == 0) { throw new OpenGammaRuntimeException("Price time series for " + security.getExternalIdBundle() + " was empty"); } final double lastMarginPrice = ts.getLatestValue(); return ((BondFuturesTransactionDefinition) definition).toDerivative(date, lastMarginPrice); } }