/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.analytics.conversion; import org.threeten.bp.LocalDate; 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.InstrumentDefinitionWithData; import com.opengamma.analytics.financial.instrument.future.FederalFundsFutureTransactionDefinition; import com.opengamma.analytics.financial.interestrate.InstrumentDerivative; import com.opengamma.core.convention.ConventionSource; import com.opengamma.core.historicaltimeseries.HistoricalTimeSeries; import com.opengamma.core.link.ConventionLink; import com.opengamma.financial.analytics.ircurve.strips.CurveNode; import com.opengamma.financial.analytics.ircurve.strips.CurveNodeWithIdentifier; import com.opengamma.financial.analytics.ircurve.strips.DeliverableSwapFutureNode; import com.opengamma.financial.analytics.ircurve.strips.RateFutureNode; import com.opengamma.financial.analytics.ircurve.strips.ZeroCouponInflationNode; import com.opengamma.financial.analytics.timeseries.HistoricalTimeSeriesBundle; import com.opengamma.financial.convention.FederalFundsFutureConvention; import com.opengamma.financial.convention.InflationLegConvention; import com.opengamma.id.ExternalId; import com.opengamma.timeseries.DoubleTimeSeries; 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; /** * Converts the curve nodes into instruments, in particular for curve calibration. */ public class CurveNodeConverter { /** * Creates the converter. */ public CurveNodeConverter() { } /** * @param conventionSource the convention source, not required * @deprecated use no-arg constructor */ @Deprecated public CurveNodeConverter(final ConventionSource conventionSource) { } /** * Given an {@link InstrumentDefinition} (the time-independent form used in the analytics library) and a valuation time, converts to the * time-dependent {@link InstrumentDerivative} form. * @param node The curve node, not null * @param definition The definition, not null * @param now The valuation time, not null * @param timeSeries A fixing time series, not null if {@link #requiresFixingSeries(CurveNode)} is true and definition is an instance of {@link InstrumentDefinitionWithData}. * @return A derivative instrument */ @SuppressWarnings("unchecked") public InstrumentDerivative getDerivative(CurveNodeWithIdentifier node, InstrumentDefinition<?> definition, ZonedDateTime now, HistoricalTimeSeriesBundle timeSeries) { ArgumentChecker.notNull(node, "node"); ArgumentChecker.notNull(definition, "definition"); ArgumentChecker.notNull(now, "now"); if (definition instanceof InstrumentDefinitionWithData<?, ?> && requiresFixingSeries(node.getCurveNode())) { if (node.getCurveNode() instanceof ZeroCouponInflationNode) { ArgumentChecker.notNull(timeSeries, "time series"); ExternalId legConvention = ((ZeroCouponInflationNode) node.getCurveNode()).getInflationLegConvention(); InflationLegConvention inflationLegConvention = ConventionLink.resolvable(legConvention, InflationLegConvention.class).resolve(); ExternalId priceIndexId = inflationLegConvention.getPriceIndexConvention(); HistoricalTimeSeries historicalTimeSeries = timeSeries.get(node.getDataField(), priceIndexId); if (historicalTimeSeries == null) { throw new OpenGammaRuntimeException("Could not get price time series for " + priceIndexId); } DoubleTimeSeries<?> ts = historicalTimeSeries.getTimeSeries(); if (ts == null) { throw new OpenGammaRuntimeException("Could not get price time series for " + priceIndexId); } int length = ts.size(); if (length == 0) { throw new OpenGammaRuntimeException("Price time series for " + priceIndexId + " was empty"); } // the timeseries is multiply by 100 because Bloomberg do not provide the right one ZonedDateTimeDoubleTimeSeries multiply = convertTimeSeries(ZoneId.of("UTC"), (LocalDateDoubleTimeSeries) ts.multiply(100)); return ((InstrumentDefinitionWithData<?, ZonedDateTimeDoubleTimeSeries[]>) definition).toDerivative( now, new ZonedDateTimeDoubleTimeSeries[] {multiply, multiply}); } if (definition instanceof FederalFundsFutureTransactionDefinition) { ArgumentChecker.notNull(timeSeries, "time series"); RateFutureNode nodeFFF = (RateFutureNode) node.getCurveNode(); FederalFundsFutureConvention conventionFFF = ConventionLink.resolvable(nodeFFF.getFutureConvention(), FederalFundsFutureConvention.class).resolve(); // Retrieving id of the underlying index. HistoricalTimeSeries historicalTimeSeriesUnderlyingIndex = timeSeries.get(node.getDataField(), conventionFFF.getIndexConvention()); if (historicalTimeSeriesUnderlyingIndex == null) { throw new OpenGammaRuntimeException( "Could not get price time series for " + conventionFFF.getIndexConvention()); } final DoubleTimeSeries<ZonedDateTime>[] tsArray = new DoubleTimeSeries[1]; tsArray[0] = convertTimeSeries(now.getZone(), historicalTimeSeriesUnderlyingIndex.getTimeSeries()); // No time series is passed for the closing price; for curve calibration only the trade price is required. InstrumentDefinitionWithData<?, DoubleTimeSeries<ZonedDateTime>[]> definitonInstWithData = //CSIGNORE (InstrumentDefinitionWithData<?, DoubleTimeSeries<ZonedDateTime>[]>) definition; //CSIGNORE return definitonInstWithData.toDerivative(now, tsArray); } if (node.getCurveNode() instanceof RateFutureNode || node.getCurveNode() instanceof DeliverableSwapFutureNode) { return ((InstrumentDefinitionWithData<?, Double>) definition).toDerivative(now, (Double) null); // No last closing price is passed; for curve calibration only the trade price is required. } throw new OpenGammaRuntimeException("Cannot handle swaps with fixings"); } return definition.toDerivative(now); } public static boolean requiresFixingSeries(CurveNode node) { /** Implementation node: fixing series are required for - inflation swaps (starting price index) - Fed Fund futures: underlying overnight index fixing (when fixing month has started) - Ibor swaps (when the UseFixing flag is true) */ return node instanceof ZeroCouponInflationNode || node instanceof RateFutureNode; // [PLAT-6430] Add case for (SwapNode) node).isUseFixings() } private static ZonedDateTimeDoubleTimeSeries convertTimeSeries(ZoneId timeZone, LocalDateDoubleTimeSeries localDateTS) { // FIXME CASE Converting a daily historical time series to an arbitrary time. Bad idea final ZonedDateTimeDoubleTimeSeriesBuilder bld = ImmutableZonedDateTimeDoubleTimeSeries.builder(timeZone); for (LocalDateDoubleEntryIterator it = localDateTS.iterator(); it.hasNext();) { LocalDate date = it.nextTime(); ZonedDateTime zdt = date.atStartOfDay(timeZone); bld.put(zdt, it.currentValueFast()); } return bld.build(); } }