/**
* Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.data.scenario;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.collect.Messages;
import com.opengamma.strata.collect.timeseries.LocalDateDoubleTimeSeries;
import com.opengamma.strata.data.MarketDataId;
import com.opengamma.strata.data.ObservableId;
/**
* A mutable builder for market data.
* <p>
* This is used to create implementations of {@link ImmutableScenarioMarketData}.
*/
public final class ImmutableScenarioMarketDataBuilder {
/**
* The number of scenarios.
*/
private int scenarioCount;
/**
* The valuation date associated with each scenario.
*/
private final MarketDataBox<LocalDate> valuationDate;
/**
* The individual items of market data.
*/
private final Map<MarketDataId<?>, MarketDataBox<?>> values = new HashMap<>();
/**
* The time-series of market data values.
*/
private final Map<ObservableId, LocalDateDoubleTimeSeries> timeSeries = new HashMap<>();
//-------------------------------------------------------------------------
ImmutableScenarioMarketDataBuilder(LocalDate valuationDate) {
ArgChecker.notNull(valuationDate, "valuationDate");
this.scenarioCount = -1;
this.valuationDate = MarketDataBox.ofSingleValue(valuationDate);
}
ImmutableScenarioMarketDataBuilder(MarketDataBox<LocalDate> valuationDate) {
ArgChecker.notNull(valuationDate, "valuationDate");
this.scenarioCount = -1;
this.valuationDate = valuationDate;
}
ImmutableScenarioMarketDataBuilder(
int scenarioCount,
MarketDataBox<LocalDate> valuationDate,
Map<? extends MarketDataId<?>, MarketDataBox<?>> values,
Map<? extends ObservableId, LocalDateDoubleTimeSeries> timeSeries) {
ArgChecker.notNegative(scenarioCount, "scenarioCount");
ArgChecker.notNull(valuationDate, "valuationDate");
ArgChecker.notNull(values, "values");
ArgChecker.notNull(timeSeries, "timeSeries");
this.scenarioCount = scenarioCount;
this.valuationDate = valuationDate;
this.values.putAll(values);
this.timeSeries.putAll(timeSeries);
}
//-------------------------------------------------------------------------
/**
* Sets the values in the builder, replacing any existing values.
*
* @param values the values
* @return this builder
*/
public ImmutableScenarioMarketDataBuilder values(Map<? extends MarketDataId<?>, ?> values) {
this.values.clear();
return addValueMap(values);
}
/**
* Sets the time-series in the builder, replacing any existing values.
*
* @param timeSeries the time-series
* @return this builder
*/
public ImmutableScenarioMarketDataBuilder timeSeries(Map<? extends ObservableId, LocalDateDoubleTimeSeries> timeSeries) {
this.timeSeries.clear();
return addTimeSeriesMap(timeSeries);
}
//-------------------------------------------------------------------------
/**
* Adds market data that is valid for all scenarios.
* <p>
* Any existing value with the same identifier will be replaced.
*
* @param id the identifier
* @param value the market data value
* @param <T> the type of the market data value
* @return this builder
*/
public <T> ImmutableScenarioMarketDataBuilder addValue(MarketDataId<T> id, T value) {
ArgChecker.notNull(id, "id");
ArgChecker.notNull(value, "value");
values.put(id, MarketDataBox.ofSingleValue(value));
return this;
}
/**
* Adds market data values that are valid for all scenarios.
* <p>
* Each value in the map is a single item of market data used in all scenarios.
* Any existing value with the same identifier will be replaced.
*
* @param values the items of market data, keyed by identifier
* @return this builder
*/
public ImmutableScenarioMarketDataBuilder addValueMap(Map<? extends MarketDataId<?>, ?> values) {
ArgChecker.notNull(values, "values");
for (Entry<? extends MarketDataId<?>, ?> entry : values.entrySet()) {
MarketDataId<?> id = entry.getKey();
Object value = entry.getValue();
MarketDataBox<?> box = MarketDataBox.ofSingleValue(value);
checkBoxType(id, box);
checkAndUpdateScenarioCount(box);
this.values.put(id, box);
}
return this;
}
//-------------------------------------------------------------------------
/**
* Adds market data for each scenario.
* <p>
* Any existing value with the same identifier will be replaced.
*
* @param id the identifier
* @param values the market data values, one for each scenario
* @param <T> the type of the market data values
* @return this builder
*/
public <T> ImmutableScenarioMarketDataBuilder addScenarioValue(MarketDataId<T> id, List<? extends T> values) {
ArgChecker.notNull(id, "id");
ArgChecker.notNull(values, "values");
MarketDataBox<? extends T> box = MarketDataBox.ofScenarioValues(values);
checkAndUpdateScenarioCount(box);
this.values.put(id, box);
return this;
}
/**
* Adds market data for each scenario.
* <p>
* Any existing value with the same identifier will be replaced.
*
* @param id the identifier
* @param value the market data values, one for each scenario
* @param <T> the type of the market data values
* @return this builder
*/
public <T> ImmutableScenarioMarketDataBuilder addScenarioValue(MarketDataId<T> id, ScenarioArray<? extends T> value) {
ArgChecker.notNull(id, "id");
ArgChecker.notNull(value, "values");
MarketDataBox<? extends T> box = MarketDataBox.ofScenarioValue(value);
checkAndUpdateScenarioCount(box);
this.values.put(id, box);
return this;
}
/**
* Adds market data values for each scenario.
* <p>
* Each value in the map contains multiple market data items, one for each scenario.
* Any existing value with the same identifier will be replaced.
*
* @param values the items of market data, keyed by identifier
* @return this builder
*/
public ImmutableScenarioMarketDataBuilder addScenarioValueMap(
Map<? extends MarketDataId<?>, ? extends ScenarioArray<?>> values) {
ArgChecker.notNull(values, "values");
for (Entry<? extends MarketDataId<?>, ? extends ScenarioArray<?>> entry : values.entrySet()) {
MarketDataId<?> id = entry.getKey();
ScenarioArray<?> value = entry.getValue();
MarketDataBox<?> box = MarketDataBox.ofScenarioValue(value);
checkBoxType(id, box);
checkAndUpdateScenarioCount(box);
this.values.put(id, box);
}
return this;
}
//-------------------------------------------------------------------------
/**
* Adds market data wrapped in a box.
* <p>
* Any existing value with the same identifier will be replaced.
*
* @param id the identifier
* @param value the market data value
* @param <T> the type of the market data value
* @return this builder
*/
public <T> ImmutableScenarioMarketDataBuilder addBox(MarketDataId<T> id, MarketDataBox<? extends T> value) {
ArgChecker.notNull(id, "id");
ArgChecker.notNull(value, "value");
checkAndUpdateScenarioCount(value);
values.put(id, value);
return this;
}
/**
* Adds market data values for each scenario.
* <p>
* Each value in the map is a market data box.
* Any existing value with the same identifier will be replaced.
*
* @param values the items of market data, keyed by identifier
* @return this builder
*/
public ImmutableScenarioMarketDataBuilder addBoxMap(
Map<? extends MarketDataId<?>, ? extends MarketDataBox<?>> values) {
ArgChecker.notNull(values, "values");
for (Entry<? extends MarketDataId<?>, ? extends MarketDataBox<?>> entry : values.entrySet()) {
MarketDataId<?> id = entry.getKey();
MarketDataBox<?> box = entry.getValue();
checkBoxType(id, box);
checkAndUpdateScenarioCount(box);
this.values.put(id, box);
}
return this;
}
//-------------------------------------------------------------------------
/**
* Adds a time-series of observable market data values.
* <p>
* Any existing time-series with the same identifier will be replaced.
*
* @param id the identifier
* @param timeSeries a time-series of observable market data values
* @return this builder
*/
public ImmutableScenarioMarketDataBuilder addTimeSeries(ObservableId id, LocalDateDoubleTimeSeries timeSeries) {
ArgChecker.notNull(id, "id");
ArgChecker.notNull(timeSeries, "timeSeries");
this.timeSeries.put(id, timeSeries);
return this;
}
/**
* Adds multiple time-series of observable market data values to the builder.
* <p>
* Any existing time-series with the same identifier will be replaced.
*
* @param timeSeriesMap the map of time-series
* @return this builder
*/
public ImmutableScenarioMarketDataBuilder addTimeSeriesMap(
Map<? extends ObservableId, LocalDateDoubleTimeSeries> timeSeriesMap) {
ArgChecker.notNull(timeSeriesMap, "timeSeriesMap");
this.timeSeries.putAll(timeSeriesMap);
return this;
}
//-------------------------------------------------------------------------
private static void checkBoxType(MarketDataId<?> id, MarketDataBox<?> box) {
if (!id.getMarketDataType().isAssignableFrom(box.getMarketDataType())) {
throw new IllegalArgumentException(Messages.format(
"Market data type {} of value {} is not compatible with the market data type of the identifier {}",
box.getMarketDataType().getName(),
box,
id.getMarketDataType().getName()));
}
}
private void checkAndUpdateScenarioCount(MarketDataBox<?> value) {
if (value.isScenarioValue()) {
if (scenarioCount == -1) {
scenarioCount = value.getScenarioCount();
} else if (value.getScenarioCount() != scenarioCount) {
throw new IllegalArgumentException(Messages.format(
"All values must have the same number of scenarios, expecting {} but received {}",
scenarioCount,
value.getScenarioCount()));
}
}
}
//-------------------------------------------------------------------------
/**
* Builds the market data.
*
* @return the market data
*/
public ImmutableScenarioMarketData build() {
if (scenarioCount == -1) {
scenarioCount = 1;
}
return new ImmutableScenarioMarketData(scenarioCount, valuationDate, values, timeSeries);
}
}