/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.master.historicaltimeseries.impl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.threeten.bp.LocalDate; import com.opengamma.id.ExternalIdBundle; import com.opengamma.id.ExternalIdBundleWithDates; import com.opengamma.id.ExternalIdSearch; import com.opengamma.id.ExternalIdSearchType; import com.opengamma.id.ObjectId; import com.opengamma.id.UniqueId; import com.opengamma.master.historicaltimeseries.HistoricalTimeSeriesInfoDocument; import com.opengamma.master.historicaltimeseries.HistoricalTimeSeriesInfoSearchRequest; import com.opengamma.master.historicaltimeseries.HistoricalTimeSeriesInfoSearchResult; import com.opengamma.master.historicaltimeseries.HistoricalTimeSeriesMaster; import com.opengamma.master.historicaltimeseries.ManageableHistoricalTimeSeries; import com.opengamma.master.historicaltimeseries.ManageableHistoricalTimeSeriesInfo; import com.opengamma.timeseries.date.localdate.ImmutableLocalDateDoubleTimeSeries; import com.opengamma.timeseries.date.localdate.LocalDateDoubleTimeSeries; import com.opengamma.util.ArgumentChecker; /** * Provides functionality to ensure that a time-series is present in a historical time-series master while avoiding * duplicates. */ public class HistoricalTimeSeriesMasterUtils { private static final Logger s_logger = LoggerFactory.getLogger(HistoricalTimeSeriesMasterUtils.class); private final HistoricalTimeSeriesMaster _htsMaster; public HistoricalTimeSeriesMasterUtils(HistoricalTimeSeriesMaster htsMaster) { _htsMaster = htsMaster; } /** * Updates an existing time-series in the master. * If the time series provided has overlaps with the existing time series, the old * versions of intersecting points will be corrected to the new ones. * After that, points later than the existing latest point of the time series will * be appended. * * @param description a description of the time-series for display purposes, not null * @param dataSource the data source, not null * @param dataProvider the data provider, not null * @param dataField the data field, not null * @param observationTime the descriptive observation time key, e.g. LONDON_CLOSE, not null * @param oId the unique identifier of the time-series to be updated, not null * @param timeSeries the time-series, not null * @return the unique identifier of the time-series */ public UniqueId writeTimeSeries(String description, String dataSource, String dataProvider, String dataField, String observationTime, ObjectId oId, LocalDateDoubleTimeSeries timeSeries) { UniqueId uId = oId.atLatestVersion(); ManageableHistoricalTimeSeries existingManageableTs = _htsMaster.getTimeSeries(uId); LocalDateDoubleTimeSeries existingTs = existingManageableTs.getTimeSeries(); if (existingTs.isEmpty()) { uId = _htsMaster.updateTimeSeriesDataPoints(oId, timeSeries); s_logger.debug("Updating time series " + oId + "[" + dataField + "] with all as currently emtpy)"); } else { // There is a non-empty matching time-series already in the master so update it to reflect the new time-series // 1: 'correct' any differences in the subseries already present LocalDateDoubleTimeSeries tsIntersection = timeSeries.subSeries(existingTs.getEarliestTime(), true, existingTs.getLatestTime(), true); if (!tsIntersection.equals(existingTs)) { s_logger.debug("Correcting time series " + oId + "[" + dataField + "] from " + existingTs.getEarliestTime() + " to " + existingTs.getLatestTime()); uId = _htsMaster.correctTimeSeriesDataPoints(oId, tsIntersection); } // 2: 'update' the time-series to add any new, later points if (existingTs.getLatestTime().isBefore(timeSeries.getLatestTime())) { LocalDateDoubleTimeSeries newSeries = timeSeries.subSeries(existingTs.getLatestTime(), false, timeSeries.getLatestTime(), true); if (newSeries.size() > 0) { s_logger.debug("Updating time series " + oId + "[" + dataField + "] from " + newSeries.getEarliestTime() + " to " + newSeries.getLatestTime()); uId = _htsMaster.updateTimeSeriesDataPoints(oId, newSeries); } } } return uId; } /** * Updates an existing time-series in the master. * @param uniqueId the unique identifier of the time-series to be updated, not null * @param timeSeries the time-series, not null * @return the unique identifier of the time-series */ public UniqueId writeTimeSeries(UniqueId uniqueId, LocalDateDoubleTimeSeries timeSeries) { ManageableHistoricalTimeSeries existingManageableTs = _htsMaster.getTimeSeries(uniqueId); LocalDateDoubleTimeSeries existingTs = existingManageableTs.getTimeSeries(); if (existingTs.isEmpty()) { _htsMaster.updateTimeSeriesDataPoints(uniqueId, timeSeries); s_logger.debug("Updating time series " + uniqueId + " with all as currently emtpy)"); } else { // There is a matching time-series already in the master so update it to reflect the new time-series // 1: 'correct' any differences in the subseries already present LocalDateDoubleTimeSeries tsIntersection = timeSeries.subSeries(existingTs.getEarliestTime(), true, existingTs.getLatestTime(), true); if (!tsIntersection.equals(existingTs)) { s_logger.debug("Correcting time series " + uniqueId + " from " + existingTs.getEarliestTime() + " to " + existingTs.getLatestTime()); uniqueId = _htsMaster.correctTimeSeriesDataPoints(uniqueId.getObjectId(), tsIntersection); } // 2: 'update' the time-series to add any new, later points if (existingTs.getLatestTime().isBefore(timeSeries.getLatestTime())) { LocalDateDoubleTimeSeries newSeries = timeSeries.subSeries(existingTs.getLatestTime(), false, timeSeries.getLatestTime(), true); if (newSeries.size() > 0) { s_logger.debug("Updating time series " + uniqueId + " from " + newSeries.getEarliestTime() + " to " + newSeries.getLatestTime()); uniqueId = _htsMaster.updateTimeSeriesDataPoints(uniqueId, newSeries); } } } return uniqueId; } //------------------------------------------------------------------------- /** * Adds or updates a time-series in the master. Can be a sub-set of the data points present and will not 'erase' * points that are missing, only supplement them. * * @param description a description of the time-series for display purposes, not null * @param dataSource the data source, not null * @param dataProvider the data provider, not null * @param dataField the data field, not null * @param observationTime the descriptive observation time key, e.g. LONDON_CLOSE, not null * @param externalIdBundle the external identifiers with which the time-series is associated, not null * @param timeSeries the time-series, not null * @return the unique identifier of the time-series */ public UniqueId writeTimeSeries(String description, String dataSource, String dataProvider, String dataField, String observationTime, ExternalIdBundle externalIdBundle, LocalDateDoubleTimeSeries timeSeries) { return writeTimeSeries(description, dataSource, dataProvider, dataField, observationTime, externalIdBundle, null, timeSeries); } /** * Adds or updates a time-series in the master. Can be a sub-set of the data points present and will not 'erase' * points that are missing, only supplement them. * * @param description a description of the time-series for display purposes, not null * @param dataSource the data source, not null * @param dataProvider the data provider, not null * @param dataField the data field, not null * @param observationTime the descriptive observation time key, e.g. LONDON_CLOSE, not null * @param externalIdBundle the external identifiers with which the time-series is associated, not null * @param externalIdSearchType the external identifier search type for matching an existing time-series, null to use the default * @param timeSeries the time-series, not null * @return the unique identifier of the time-series */ public UniqueId writeTimeSeries(String description, String dataSource, String dataProvider, String dataField, String observationTime, ExternalIdBundle externalIdBundle, ExternalIdSearchType externalIdSearchType, LocalDateDoubleTimeSeries timeSeries) { ArgumentChecker.notNull(description, "description"); ArgumentChecker.notNull(dataSource, "dataSource"); ArgumentChecker.notNull(dataProvider, "dataProvider"); ArgumentChecker.notNull(dataField, "dataField"); ArgumentChecker.notNull(observationTime, "observationTime"); ArgumentChecker.notNull(externalIdBundle, "externalIdBundle"); ArgumentChecker.notNull(timeSeries, "timeSeries"); HistoricalTimeSeriesInfoSearchRequest htsSearchReq = new HistoricalTimeSeriesInfoSearchRequest(); ExternalIdSearch idSearch = ExternalIdSearch.of(externalIdBundle); if (externalIdSearchType != null) { idSearch = idSearch.withSearchType(externalIdSearchType); } htsSearchReq.setExternalIdSearch(idSearch); htsSearchReq.setDataSource(dataSource); htsSearchReq.setDataProvider(dataProvider); htsSearchReq.setDataField(dataField); htsSearchReq.setObservationTime(observationTime); HistoricalTimeSeriesInfoSearchResult searchResult = _htsMaster.search(htsSearchReq); if (searchResult.getDocuments().size() > 0) { if (searchResult.getDocuments().size() > 1) { s_logger.warn("Found multiple time-series matching search. Will only update the first. Search {} returned {}", htsSearchReq, searchResult.getInfoList()); } // update existing time series HistoricalTimeSeriesInfoDocument existingTsDoc = searchResult.getFirstDocument(); return writeTimeSeries(description, dataSource, dataProvider, dataField, observationTime, existingTsDoc.getObjectId(), timeSeries); } else { // add new time series ManageableHistoricalTimeSeriesInfo info = new ManageableHistoricalTimeSeriesInfo(); info.setDataField(dataField); info.setDataSource(dataSource); info.setDataProvider(dataProvider); info.setObservationTime(observationTime); info.setExternalIdBundle(ExternalIdBundleWithDates.of(externalIdBundle)); info.setName(description); HistoricalTimeSeriesInfoDocument htsInfoDoc = new HistoricalTimeSeriesInfoDocument(); htsInfoDoc.setInfo(info); HistoricalTimeSeriesInfoDocument addedInfoDoc = _htsMaster.add(htsInfoDoc); s_logger.debug("Adding time series " + externalIdBundle + " from " + timeSeries.getEarliestTime() + " to " + timeSeries.getLatestTime()); return _htsMaster.updateTimeSeriesDataPoints(addedInfoDoc.getInfo().getTimeSeriesObjectId(), timeSeries); } } /** * Adds or updates a time-series in the master. Will not "erase" any existing point, just * used to add a new point. * * @param description a description of the time-series for display purposes, not null * @param dataSource the data source, not null * @param dataProvider the data provider, not null * @param dataField the data field, not null * @param observationTime the descriptive observation time key, e.g. LONDON_CLOSE, not null * @param externalIdBundle the external identifiers with which the time-series is associated, not null * @param date the date, not null * @param value the value, not null * @return the unique identifier of the time-series */ public UniqueId writeTimeSeriesPoint(String description, String dataSource, String dataProvider, String dataField, String observationTime, ExternalIdBundle externalIdBundle, LocalDate date, double value) { LocalDateDoubleTimeSeries ts = ImmutableLocalDateDoubleTimeSeries.of(date, value); return writeTimeSeries(description, dataSource, dataProvider, dataField, observationTime, externalIdBundle, ts); } }