/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.sesame; 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.InstrumentDefinitionWithData; import com.opengamma.analytics.financial.instrument.future.FederalFundsFutureTransactionDefinition; import com.opengamma.analytics.financial.interestrate.InstrumentDerivative; 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.convention.FederalFundsFutureConvention; import com.opengamma.financial.convention.InflationLegConvention; import com.opengamma.id.ExternalId; import com.opengamma.id.ExternalIdBundle; import com.opengamma.sesame.component.RetrievalPeriod; import com.opengamma.sesame.marketdata.FieldName; import com.opengamma.sesame.marketdata.HistoricalMarketDataFn; 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; import com.opengamma.util.result.Result; import com.opengamma.util.time.LocalDateRange; /** * Default curve node converter implementation. */ public class DefaultCurveNodeConverterFn implements CurveNodeConverterFn { private final HistoricalMarketDataFn _historicalMarketDataFn; // TODO where should this come from? private final Period _timeSeriesDuration; // TODO use a real Period when Joda supports it public DefaultCurveNodeConverterFn(HistoricalMarketDataFn historicalMarketDataFn, RetrievalPeriod timeSeriesDuration) { _timeSeriesDuration = ArgumentChecker.notNull(timeSeriesDuration, "timeSeriesDuration").getRetrievalPeriod(); _historicalMarketDataFn = ArgumentChecker.notNull(historicalMarketDataFn, "historicalMarketDataFn"); } @SuppressWarnings("unchecked") @Override public Result<InstrumentDerivative> getDerivative(Environment env, CurveNodeWithIdentifier node, InstrumentDefinition<?> definition, ZonedDateTime valuationTime) { ArgumentChecker.notNull(node, "node"); ArgumentChecker.notNull(definition, "definition"); ArgumentChecker.notNull(valuationTime, "time"); if (definition instanceof InstrumentDefinitionWithData<?, ?> && requiresFixingSeries(node.getCurveNode())) { String dataField = node.getDataField(); if (node.getCurveNode() instanceof ZeroCouponInflationNode) { ExternalId priceIndexId; ZeroCouponInflationNode zeroCouponInflationNode = (ZeroCouponInflationNode) node.getCurveNode(); ExternalId conventionId = zeroCouponInflationNode.getInflationLegConvention(); ConventionLink<InflationLegConvention> conventionLink = ConventionLink.resolvable(conventionId, InflationLegConvention.class); InflationLegConvention convention = conventionLink.resolve(); priceIndexId = convention.getPriceIndexConvention(); LocalDateRange dateRange = LocalDateRange.of(valuationTime.toLocalDate().minus(_timeSeriesDuration), valuationTime.toLocalDate(), true); Result<LocalDateDoubleTimeSeries> timeSeriesResult = _historicalMarketDataFn.getValues(env, priceIndexId.toBundle(), FieldName.of(dataField), dateRange); if (!timeSeriesResult.isSuccess()) { return Result.failure(timeSeriesResult); } LocalDateDoubleTimeSeries timeSeries = timeSeriesResult.getValue(); // the timeseries is multiply by 100 because Bloomberg do not provide the right one ZonedDateTimeDoubleTimeSeries scaledSeries = convertTimeSeries(ZoneId.of("UTC"), timeSeries.multiply(100)); InstrumentDefinitionWithData<?, ZonedDateTimeDoubleTimeSeries[]> definitionWithData = (InstrumentDefinitionWithData<?, ZonedDateTimeDoubleTimeSeries[]>) definition; ZonedDateTimeDoubleTimeSeries[] timeSeriesArray = {scaledSeries, scaledSeries}; InstrumentDerivative derivative = definitionWithData.toDerivative(valuationTime, timeSeriesArray); return Result.success(derivative); } if (definition instanceof FederalFundsFutureTransactionDefinition) { RateFutureNode nodeFFF = (RateFutureNode) node.getCurveNode(); FederalFundsFutureConvention conventionFFF = ConventionLink.resolvable(nodeFFF.getFutureConvention(), FederalFundsFutureConvention.class).resolve(); LocalDateRange dateRange = LocalDateRange.of(valuationTime.toLocalDate().minus(_timeSeriesDuration), valuationTime.toLocalDate(), true); ExternalIdBundle indexConventionId = conventionFFF.getIndexConvention().toBundle(); Result<LocalDateDoubleTimeSeries> timeSeriesResult = _historicalMarketDataFn.getValues(env, indexConventionId, FieldName.of(dataField), dateRange); if (!timeSeriesResult.isSuccess()) { return Result.failure(timeSeriesResult); } ZonedDateTimeDoubleTimeSeries convertedSeries = convertTimeSeries(valuationTime.getZone(), timeSeriesResult.getValue()); // No time series is passed for the closing price; for curve calibration only the trade price is required. @SuppressWarnings("rawtypes") // handle difference between Eclipse and Javac via raw types InstrumentDefinitionWithData definitionInstWithData = (InstrumentDefinitionWithData) definition; return Result.success(definitionInstWithData.toDerivative(valuationTime, new DoubleTimeSeries[]{convertedSeries})); } if (node.getCurveNode() instanceof RateFutureNode || node.getCurveNode() instanceof DeliverableSwapFutureNode) { @SuppressWarnings("rawtypes") // handle difference between Eclipse and Javac via raw types InstrumentDefinitionWithData definitionWithData = (InstrumentDefinitionWithData) definition; return Result.success(definitionWithData.toDerivative(valuationTime, (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"); } @SuppressWarnings("rawtypes") // handle difference between Eclipse and Javac via raw types InstrumentDefinition untypedDefinition = (InstrumentDefinition) definition; return Result.success(untypedDefinition.toDerivative(valuationTime)); } 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 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(); } }