com.opengamma.strata.calc.marketdata.BuiltScenarioMarketDataBuilder Maven / Gradle / Ivy
Show all versions of strata-calc Show documentation
/*
* Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.calc.marketdata;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.Map;
import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.collect.Messages;
import com.opengamma.strata.collect.result.Failure;
import com.opengamma.strata.collect.result.Result;
import com.opengamma.strata.collect.timeseries.LocalDateDoubleTimeSeries;
import com.opengamma.strata.data.MarketDataId;
import com.opengamma.strata.data.ObservableId;
import com.opengamma.strata.data.scenario.ImmutableScenarioMarketData;
import com.opengamma.strata.data.scenario.ImmutableScenarioMarketDataBuilder;
import com.opengamma.strata.data.scenario.MarketDataBox;
/**
* A mutable builder for building up {@link BuiltScenarioMarketData} instances.
*/
final class BuiltScenarioMarketDataBuilder {
/** The valuation date associated with the market data. */
private MarketDataBox valuationDate = MarketDataBox.empty();
/** The number of scenarios for which this builder contains market data. */
private int scenarioCount;
/** The single value market data items, keyed by ID. */
private final Map, MarketDataBox>> values = new HashMap<>();
/** Time series of observable market data values, keyed by ID. */
private final Map timeSeries = new HashMap<>();
/** Details of failures when building single market data values. */
private final Map, Failure> valueFailures = new HashMap<>();
/** Details of failures when building time series of market data values. */
private final Map, Failure> timeSeriesFailures = new HashMap<>();
/**
* Creates a builder pre-populated with the valuation date.
*
* @param valuationDate the valuation date associated with the market data
*/
BuiltScenarioMarketDataBuilder(LocalDate valuationDate) {
ArgChecker.notNull(valuationDate, "valuationDate");
this.valuationDate = MarketDataBox.ofSingleValue(valuationDate);
updateScenarioCount(this.valuationDate);
}
/**
* Creates a builder pre-populated with the valuation date.
*
* @param valuationDate the valuation date associated with the market data
*/
BuiltScenarioMarketDataBuilder(MarketDataBox valuationDate) {
ArgChecker.notNull(valuationDate, "valuationDate");
if (valuationDate.getScenarioCount() == 0) {
throw new IllegalArgumentException("Valuation date must not be empty");
}
updateScenarioCount(valuationDate);
this.valuationDate = valuationDate;
}
/**
* Creates a builder pre-populated with data.
*
* @param valuationDate the valuation date associated with the market data
* @param scenarioCount the number of scenarios for which this builder contains market data
* @param values the single value market data items, keyed by ID
* @param timeSeries time series of observable market data values, keyed by ID
* @param valueFailures details of failures encountered when building market data values
* @param timeSeriesFailures details of failures encountered when building time series
*/
BuiltScenarioMarketDataBuilder(
MarketDataBox valuationDate,
int scenarioCount,
Map extends MarketDataId>, MarketDataBox>> values,
Map extends ObservableId, LocalDateDoubleTimeSeries> timeSeries,
Map, Failure> valueFailures,
Map, Failure> timeSeriesFailures) {
this.valuationDate = ArgChecker.notNull(valuationDate, "valuationDate");
this.scenarioCount = scenarioCount;
this.values.putAll(values);
this.timeSeries.putAll(timeSeries);
this.valueFailures.putAll(valueFailures);
this.timeSeriesFailures.putAll(timeSeriesFailures);
}
/**
* Adds a single item of market data, replacing any existing value with the same ID.
*
* @param id the ID of the market data
* @param value the market data value
* @param the type of the market data value
* @return this builder
*/
BuiltScenarioMarketDataBuilder addValue(MarketDataId id, T value) {
ArgChecker.notNull(id, "id");
ArgChecker.notNull(value, "value");
values.put(id, MarketDataBox.ofSingleValue(value));
return this;
}
/**
* Adds a single market data box, replacing any existing box with the same ID.
*
* The type of the box is checked to ensure it is compatible with the ID.
*
* @param id the ID of the market data
* @param box the market data box
* @return this builder
*/
BuiltScenarioMarketDataBuilder addBox(MarketDataId> id, MarketDataBox> box) {
ArgChecker.notNull(id, "id");
ArgChecker.notNull(box, "box");
updateScenarioCount(box);
checkBoxType(id, box);
values.put(id, box);
return this;
}
//-------------------------------------------------------------------------
/**
* Adds a result for a single item of market data, replacing any existing value with the same ID.
*
* @param id the ID of the market data
* @param result a result containing the market data value or details of why it could not be provided
* @param the type of the market data value
* @return this builder
*/
BuiltScenarioMarketDataBuilder addResult(MarketDataId id, Result> result) {
ArgChecker.notNull(id, "id");
ArgChecker.notNull(result, "result");
if (result.isSuccess()) {
MarketDataBox> box = result.getValue();
checkBoxType(id, box);
updateScenarioCount(box);
values.put(id, box);
valueFailures.remove(id);
} else {
valueFailures.put(id, result.getFailure());
values.remove(id);
}
return this;
}
//-------------------------------------------------------------------------
/**
* Adds a time series of observable market data values, replacing any existing time series with the same ID.
*
* @param id the ID of the values
* @param timeSeries a time series of observable market data values
* @return this builder
*/
BuiltScenarioMarketDataBuilder addTimeSeries(ObservableId id, LocalDateDoubleTimeSeries timeSeries) {
ArgChecker.notNull(id, "id");
ArgChecker.notNull(timeSeries, "timeSeries");
this.timeSeries.put(id, timeSeries);
return this;
}
/**
* Adds a time series of observable market data values, replacing any existing time series with the same ID.
*
* @param id the ID of the values
* @param result a time series of observable market data values
* @return this builder
*/
BuiltScenarioMarketDataBuilder addTimeSeriesResult(ObservableId id, Result result) {
ArgChecker.notNull(id, "id");
ArgChecker.notNull(result, "result");
if (result.isSuccess()) {
timeSeries.put(id, result.getValue());
timeSeriesFailures.remove(id);
} else {
timeSeriesFailures.put(id, result.getFailure());
timeSeries.remove(id);
}
return this;
}
//-------------------------------------------------------------------------
/**
* Builds a set of market data from the data in this builder.
*
* It is possible to continue to add more data to a builder after calling {@code build()}.
*
* @return a set of market data from the data in this builder
*/
BuiltScenarioMarketData build() {
if (valuationDate.getScenarioCount() == 0) {
// This isn't checked in the main class otherwise it would be impossible to have an empty instance
throw new IllegalArgumentException("Valuation date must be specified");
}
ImmutableScenarioMarketDataBuilder builder = ImmutableScenarioMarketData.builder(valuationDate)
.addBoxMap(values)
.addTimeSeriesMap(timeSeries);
return new BuiltScenarioMarketData(builder.build(), valueFailures, timeSeriesFailures);
}
//-------------------------------------------------------------------------
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 updateScenarioCount(MarketDataBox> box) {
// If the box has a single value then it can be used with any number of scenarios - the same value is used
// for all scenarios.
if (box.isSingleValue()) {
if (scenarioCount == 0) {
scenarioCount = 1;
}
return;
}
int scenarioCount = box.getScenarioCount();
if (this.scenarioCount == 0 || this.scenarioCount == 1) {
this.scenarioCount = scenarioCount;
return;
}
if (scenarioCount != this.scenarioCount) {
throw new IllegalArgumentException(Messages.format(
"All values must have the same number of scenarios, expecting {} but received {}",
this.scenarioCount,
scenarioCount));
}
}
}