/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.sesame.component;
import static org.mockito.Mockito.mock;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.threeten.bp.ZonedDateTime;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import com.google.common.util.concurrent.MoreExecutors;
import com.opengamma.core.config.ConfigSource;
import com.opengamma.core.config.impl.ConfigItem;
import com.opengamma.core.convention.ConventionSource;
import com.opengamma.core.historicaltimeseries.HistoricalTimeSeriesSource;
import com.opengamma.core.holiday.Holiday;
import com.opengamma.core.holiday.HolidaySource;
import com.opengamma.core.legalentity.LegalEntitySource;
import com.opengamma.core.region.Region;
import com.opengamma.core.region.RegionSource;
import com.opengamma.core.security.SecuritySource;
import com.opengamma.financial.convention.ConventionBundleSource;
import com.opengamma.financial.convention.DefaultConventionBundleSource;
import com.opengamma.financial.convention.InMemoryConventionBundleMaster;
import com.opengamma.id.ExternalIdBundleWithDates;
import com.opengamma.id.ObjectId;
import com.opengamma.id.UniqueIdentifiable;
import com.opengamma.master.config.ConfigDocument;
import com.opengamma.master.config.ConfigMaster;
import com.opengamma.master.config.impl.InMemoryConfigMaster;
import com.opengamma.master.config.impl.MasterConfigSource;
import com.opengamma.master.convention.ConventionDocument;
import com.opengamma.master.convention.ConventionMaster;
import com.opengamma.master.convention.ManageableConvention;
import com.opengamma.master.convention.impl.InMemoryConventionMaster;
import com.opengamma.master.convention.impl.MasterConventionSource;
import com.opengamma.master.historicaltimeseries.HistoricalTimeSeriesInfoDocument;
import com.opengamma.master.historicaltimeseries.HistoricalTimeSeriesMaster;
import com.opengamma.master.historicaltimeseries.HistoricalTimeSeriesResolver;
import com.opengamma.master.historicaltimeseries.HistoricalTimeSeriesSelector;
import com.opengamma.master.historicaltimeseries.ManageableHistoricalTimeSeriesInfo;
import com.opengamma.master.historicaltimeseries.impl.DefaultHistoricalTimeSeriesResolver;
import com.opengamma.master.historicaltimeseries.impl.DefaultHistoricalTimeSeriesSelector;
import com.opengamma.master.historicaltimeseries.impl.InMemoryHistoricalTimeSeriesMaster;
import com.opengamma.master.historicaltimeseries.impl.MasterHistoricalTimeSeriesSource;
import com.opengamma.master.holiday.HolidayDocument;
import com.opengamma.master.holiday.HolidayMaster;
import com.opengamma.master.holiday.impl.InMemoryHolidayMaster;
import com.opengamma.master.holiday.impl.MasterHolidaySource;
import com.opengamma.master.region.RegionDocument;
import com.opengamma.master.region.RegionMaster;
import com.opengamma.master.region.impl.InMemoryRegionMaster;
import com.opengamma.master.region.impl.MasterRegionSource;
import com.opengamma.master.security.ManageableSecurity;
import com.opengamma.master.security.SecurityDocument;
import com.opengamma.master.security.SecurityMaster;
import com.opengamma.master.security.impl.InMemorySecurityMaster;
import com.opengamma.master.security.impl.MasterSecuritySource;
import com.opengamma.sesame.EngineTestUtils;
import com.opengamma.sesame.config.ViewConfig;
import com.opengamma.sesame.engine.CalculationArguments;
import com.opengamma.sesame.engine.DefaultEngine;
import com.opengamma.sesame.engine.Engine;
import com.opengamma.sesame.engine.Results;
import com.opengamma.sesame.engine.ViewFactory;
import com.opengamma.sesame.engine.ViewInputs;
import com.opengamma.sesame.function.AvailableImplementations;
import com.opengamma.sesame.function.AvailableOutputs;
import com.opengamma.sesame.marketdata.CompositeMarketDataFactory;
import com.opengamma.sesame.marketdata.HistoricalMarketDataFactory;
import com.opengamma.sesame.marketdata.HtsRequestKey;
import com.opengamma.sesame.marketdata.builders.MarketDataEnvironmentFactory;
import com.opengamma.sesame.marketdata.builders.RawMarketDataBuilder;
import com.opengamma.timeseries.date.localdate.ImmutableLocalDateDoubleTimeSeries;
import com.opengamma.timeseries.date.localdate.LocalDateDoubleTimeSeries;
import com.opengamma.timeseries.date.localdate.LocalDateDoubleTimeSeriesBuilder;
import com.opengamma.util.ArgumentChecker;
/**
* Enables the running of a view using the data captured from a
* previous run of the view. This means a view can be rerun
* indefinitely and does not need external access to market data,
* trade data, config data etc. This is useful for performing
* regression testing.
*/
public class CapturedResultsLoader {
private final ViewInputs _viewInputs;
private final AvailableOutputs _availableOutputs;
private final AvailableImplementations _availableImplementations;
private final Multimap<String, ConfigItem<?>> additionalConfigData = HashMultimap.create();
/**
* Creates a new loader.
*
* @param viewInputs the inputs recorded for the view
* @param availableOutputs the available outputs
* @param availableImplementations the available implementations
*/
public CapturedResultsLoader(ViewInputs viewInputs,
AvailableOutputs availableOutputs,
AvailableImplementations availableImplementations) {
_viewInputs = ArgumentChecker.notNull(viewInputs, "viewInputs");
_availableOutputs = ArgumentChecker.notNull(availableOutputs, "availableOutputs");
_availableImplementations = ArgumentChecker.notNull(availableImplementations, "availableImplementations");
}
/**
* Run the view using the captured data, returning the output results.
*
* @return the results of running the view
*/
public Results runViewFromInputs() {
// Now build the config source, market data sources etc
Multimap<Class<?>, UniqueIdentifiable> configData = _viewInputs.getConfigData();
ImmutableMap<Class<?>, Object> components = createComponents(configData);
ViewFactory viewFactory =
EngineTestUtils.createViewFactory(components, _availableOutputs, _availableImplementations);
ZonedDateTime valTime = _viewInputs.getValuationTime();
HistoricalTimeSeriesSource timeSeriesSource = createTimeSeriesSource();
HistoricalMarketDataFactory historicalMarketDataFactory =
new HistoricalMarketDataFactory(timeSeriesSource, "dataSource", null);
CompositeMarketDataFactory marketDataFactory = new CompositeMarketDataFactory(historicalMarketDataFactory);
RawMarketDataBuilder rawMarketDataBuilder = new RawMarketDataBuilder(timeSeriesSource, "dataSource", null);
Engine engine =
new DefaultEngine(
viewFactory,
new MarketDataEnvironmentFactory(marketDataFactory, rawMarketDataBuilder),
MoreExecutors.sameThreadExecutor());
CalculationArguments calculationArguments = CalculationArguments.builder().valuationTime(valTime).build();
ViewConfig viewConfig = _viewInputs.getViewConfig();
List<Object> trades = _viewInputs.getTradeInputs();
return engine.runView(viewConfig, calculationArguments, _viewInputs.getMarketDataEnvironment(), trades);
}
private HistoricalTimeSeriesSource createTimeSeriesSource() {
HistoricalTimeSeriesMaster master = new InMemoryHistoricalTimeSeriesMaster();
HistoricalTimeSeriesSelector timeSeriesSelector = new TimeSeriesSelector();
HistoricalTimeSeriesResolver timeSeriesResolver =
new DefaultHistoricalTimeSeriesResolver(timeSeriesSelector, master);
Set<Map.Entry<HtsRequestKey, Collection<LocalDateDoubleTimeSeries>>> entries =
_viewInputs.getHtsData().asMap().entrySet();
for (Map.Entry<HtsRequestKey, Collection<LocalDateDoubleTimeSeries>> entry : entries) {
HtsRequestKey request = entry.getKey();
Collection<LocalDateDoubleTimeSeries> series = entry.getValue();
LocalDateDoubleTimeSeries timeSeries = mergeTimeSeries(series);
ManageableHistoricalTimeSeriesInfo info = new ManageableHistoricalTimeSeriesInfo();
info.setExternalIdBundle(ExternalIdBundleWithDates.of(request.getIdentifierBundle()));
info.setDataField(request.getDataField());
info.setDataSource("dataSource");
info.setDataProvider("dataProvider");
info.setObservationTime("observationTime");
HistoricalTimeSeriesInfoDocument infoDoc = new HistoricalTimeSeriesInfoDocument(info);
HistoricalTimeSeriesInfoDocument fixingFedFundDoc = master.add(infoDoc);
master.updateTimeSeriesDataPoints(fixingFedFundDoc.getObjectId(), timeSeries);
}
return new MasterHistoricalTimeSeriesSource(master, timeSeriesResolver);
}
private LocalDateDoubleTimeSeries mergeTimeSeries(Collection<LocalDateDoubleTimeSeries> timeSeries) {
LocalDateDoubleTimeSeriesBuilder builder = ImmutableLocalDateDoubleTimeSeries.builder();
for (LocalDateDoubleTimeSeries series : timeSeries) {
builder.putAll(series);
}
return builder.build();
}
private ImmutableMap<Class<?>, Object> createComponents(Multimap<Class<?>, UniqueIdentifiable> configData) {
ConfigMaster configMaster = new InMemoryConfigMaster();
for (Map.Entry<String, ConfigItem<?>> entry : additionalConfigData.entries()) {
ConfigDocument document = new ConfigDocument(entry.getValue());
document.setName(entry.getKey());
configMaster.add(document);
}
for (UniqueIdentifiable item : configData.get(ConfigSource.class)) {
configMaster.add(new ConfigDocument((ConfigItem<?>) item));
}
ConfigSource configSource = new MasterConfigSource(configMaster);
SecurityMaster securityMaster = new InMemorySecurityMaster();
for (UniqueIdentifiable item : configData.get(SecuritySource.class)) {
securityMaster.add(new SecurityDocument((ManageableSecurity) item));
}
SecuritySource securitySource = new MasterSecuritySource(securityMaster);
ConventionMaster conventionMaster = new InMemoryConventionMaster();
for (UniqueIdentifiable item : configData.get(ConventionSource.class)) {
conventionMaster.add(new ConventionDocument((ManageableConvention) item));
}
ConventionSource conventionSource = new MasterConventionSource(conventionMaster);
HolidayMaster holidayMaster = new InMemoryHolidayMaster();
for (UniqueIdentifiable item : configData.get(HolidaySource.class)) {
holidayMaster.add(new HolidayDocument((Holiday) item));
}
HolidaySource holidaySource = new MasterHolidaySource(holidayMaster);
RegionMaster regionMaster = new InMemoryRegionMaster();
for (UniqueIdentifiable item : configData.get(RegionSource.class)) {
regionMaster.add(new RegionDocument((Region) item));
}
RegionSource regionSource = new MasterRegionSource(regionMaster);
ConventionBundleSource conventionBundleSource =
new DefaultConventionBundleSource(new InMemoryConventionBundleMaster());
InMemoryHistoricalTimeSeriesMaster htsMaster = new InMemoryHistoricalTimeSeriesMaster();
HistoricalTimeSeriesResolver resolver = new DefaultHistoricalTimeSeriesResolver(
new DefaultHistoricalTimeSeriesSelector(configSource), htsMaster);
HistoricalTimeSeriesSource historicalTimeSeriesSource =
new MasterHistoricalTimeSeriesSource(htsMaster, resolver);
Multimap<HtsRequestKey, LocalDateDoubleTimeSeries> htsData = _viewInputs.getHtsData();
for (HtsRequestKey htsKey : htsData.keySet()) {
ManageableHistoricalTimeSeriesInfo htsInfo = new ManageableHistoricalTimeSeriesInfo();
htsInfo.setExternalIdBundle(ExternalIdBundleWithDates.of(htsKey.getIdentifierBundle()));
htsInfo.setDataSource(htsKey.getDataSource() == null ? "Blah" : htsKey.getDataSource());
htsInfo.setDataProvider(htsKey.getDataProvider() == null ? "Blah" : htsKey.getDataProvider());
htsInfo.setObservationTime("blah");
htsInfo.setDataField(htsKey.getDataField());
ObjectId objectId = htsMaster.add(new HistoricalTimeSeriesInfoDocument(htsInfo)).getObjectId();
Collection<LocalDateDoubleTimeSeries> series = htsData.get(htsKey);
if (series.size() == 1) {
htsMaster.updateTimeSeriesDataPoints(objectId, series.iterator().next());
} else {
List<LocalDateDoubleTimeSeries> sorted = new ArrayList<>(series);
Collections.sort(sorted, new Comparator<LocalDateDoubleTimeSeries>() {
@Override
public int compare(LocalDateDoubleTimeSeries o1, LocalDateDoubleTimeSeries o2) {
return o1.getEarliestTimeFast() - o2.getEarliestTimeFast();
}
});
int previousEnd = Integer.MIN_VALUE;
for (LocalDateDoubleTimeSeries timeSeries : sorted) {
if (timeSeries.getEarliestTimeFast() > previousEnd) {
htsMaster.updateTimeSeriesDataPoints(objectId, timeSeries);
previousEnd = timeSeries.getLatestTimeFast();
} else if (timeSeries.getLatestTimeFast() > previousEnd) {
// This timeseries overlaps with the previous, we
// need to extract the dates from this one that weren't
// in the previous.
// We need to skip the first date (as that is already included
// in the data) but ensure we include the end date.
LocalDateDoubleTimeSeries subSeries =
timeSeries.subSeriesFast(previousEnd, false, timeSeries.getLatestTimeFast(), true);
htsMaster.updateTimeSeriesDataPoints(objectId, subSeries);
previousEnd = timeSeries.getLatestTimeFast();
}
}
}
}
return ImmutableMap.<Class<?>, Object>builder()
.put(ConfigSource.class, configSource)
.put(ConventionSource.class, conventionSource)
.put(SecuritySource.class, securitySource)
.put(HolidaySource.class, holidaySource)
.put(RegionSource.class, regionSource)
.put(HistoricalTimeSeriesSource.class, historicalTimeSeriesSource)
.put(HistoricalTimeSeriesResolver.class, resolver)
.put(ConventionBundleSource.class, conventionBundleSource)
// TODO - we need a capturing legal entity source but don't have one at present
.put(LegalEntitySource.class, mock(LegalEntitySource.class))
.build();
}
/**
* Add extra config data items. Intended for items that cannot currently
* be captured by the ViewInputs e.g. config links.
*
* @param data config data to be added
*/
public void addExtraConfigData(ConfigItem<?> data) {
addExtraConfigData(data.getName(), data);
}
/**
* Add extra config data items with a specific name. Intended for items
* that cannot currently be captured by the ViewInputs e.g. config links.
*
* @param name the name for the config item
* @param data config data to be added
*/
public void addExtraConfigData(String name, ConfigItem<?> data) {
additionalConfigData.put(ArgumentChecker.notEmpty(name, "name"), ArgumentChecker.notNull(data, "data"));
}
private static class TimeSeriesSelector implements HistoricalTimeSeriesSelector {
@Override
public ManageableHistoricalTimeSeriesInfo select(Collection<ManageableHistoricalTimeSeriesInfo> candidates,
String selectionKey) {
return Iterables.getFirst(candidates, null);
}
}
}