/**
* 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 static com.opengamma.strata.collect.Guavate.toImmutableMap;
import static com.opengamma.strata.collect.TestHelper.assertThrows;
import static com.opengamma.strata.collect.TestHelper.date;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.basics.StandardId;
import com.opengamma.strata.collect.result.FailureReason;
import com.opengamma.strata.collect.result.Result;
import com.opengamma.strata.collect.timeseries.LocalDateDoubleTimeSeries;
import com.opengamma.strata.data.FieldName;
import com.opengamma.strata.data.ImmutableMarketData;
import com.opengamma.strata.data.MarketData;
import com.opengamma.strata.data.MarketDataId;
import com.opengamma.strata.data.ObservableId;
import com.opengamma.strata.data.ObservableSource;
import com.opengamma.strata.data.scenario.MarketDataBox;
import com.opengamma.strata.data.scenario.ScenarioMarketData;
import com.opengamma.strata.data.scenario.ScenarioPerturbation;
@Test
public class DefaultMarketDataFactoryTest {
private static final ReferenceData REF_DATA = ReferenceData.standard();
private static final MarketDataConfig MARKET_DATA_CONFIG = MarketDataConfig.empty();
/**
* Tests building time series from requirements.
*/
public void buildTimeSeries() {
TestObservableId id1 = TestObservableId.of("1");
TestObservableId id2 = TestObservableId.of("2");
LocalDateDoubleTimeSeries timeSeries1 = LocalDateDoubleTimeSeries.builder()
.put(date(2011, 3, 8), 1)
.put(date(2011, 3, 9), 2)
.put(date(2011, 3, 10), 3)
.build();
LocalDateDoubleTimeSeries timeSeries2 = LocalDateDoubleTimeSeries.builder()
.put(date(2012, 4, 8), 10)
.put(date(2012, 4, 9), 20)
.put(date(2012, 4, 10), 30)
.build();
Map<ObservableId, LocalDateDoubleTimeSeries> timeSeries = ImmutableMap.of(id1, timeSeries1, id2, timeSeries2);
MarketDataFactory factory =
MarketDataFactory.of(ObservableDataProvider.none(), new TestTimeSeriesProvider(timeSeries));
MarketDataRequirements requirements = MarketDataRequirements.builder()
.addTimeSeries(id1, id2)
.build();
MarketData suppliedData = MarketData.empty(date(2011, 3, 8));
BuiltMarketData marketData = factory.create(requirements, MARKET_DATA_CONFIG, suppliedData, REF_DATA);
assertThat(marketData.getTimeSeries(id1)).isEqualTo(timeSeries1);
assertThat(marketData.getTimeSeries(id2)).isEqualTo(timeSeries2);
assertThat(marketData.getTimeSeriesIds()).isEqualTo(ImmutableSet.of(id1, id2));
}
/**
* Tests non-observable market data values supplied by the user are included in the results.
*/
public void buildSuppliedNonObservableValues() {
TestId id1 = new TestId("1");
TestId id2 = new TestId("2");
MarketData suppliedData = ImmutableMarketData.builder(date(2011, 3, 8))
.addValue(id1, "foo")
.addValue(id2, "bar")
.build();
MarketDataFactory factory =
MarketDataFactory.of(ObservableDataProvider.none(), new TestTimeSeriesProvider(ImmutableMap.of()));
MarketDataRequirements requirements = MarketDataRequirements.builder()
.addValues(id1, id2)
.build();
BuiltMarketData marketData = factory.create(requirements, MARKET_DATA_CONFIG, suppliedData, REF_DATA);
assertThat(marketData.getValue(id1)).isEqualTo("foo");
assertThat(marketData.getValue(id2)).isEqualTo("bar");
}
/**
* Tests building single values using market data functions.
*/
public void buildNonObservableValues() {
ObservableId idA = new TestIdA("1");
MarketDataId<?> idC = new TestIdC("1");
LocalDateDoubleTimeSeries timeSeries = LocalDateDoubleTimeSeries.builder()
.put(date(2012, 4, 8), 10)
.put(date(2012, 4, 9), 20)
.put(date(2012, 4, 10), 30)
.build();
MarketData suppliedData = ImmutableMarketData.builder(date(2011, 3, 8))
.addTimeSeries(idA, timeSeries)
.build();
MarketDataFactory factory = MarketDataFactory.of(
ObservableDataProvider.none(),
new TestTimeSeriesProvider(ImmutableMap.of()),
new TestMarketDataFunctionC());
MarketDataRequirements requirements = MarketDataRequirements.builder()
.addValues(idC)
.build();
BuiltMarketData marketData = factory.create(requirements, MARKET_DATA_CONFIG, suppliedData, REF_DATA);
assertThat(marketData.getValue(idC)).isEqualTo(new TestMarketDataC(timeSeries));
}
/**
* Tests building observable market data values.
*/
public void buildObservableValues() {
MarketDataFactory factory = MarketDataFactory.of(
new TestObservableDataProvider(),
new TestTimeSeriesProvider(ImmutableMap.of()));
MarketData suppliedData = MarketData.empty(date(2011, 3, 8));
TestObservableId id1 = TestObservableId.of(StandardId.of("reqs", "a"));
TestObservableId id2 = TestObservableId.of(StandardId.of("reqs", "b"));
MarketDataRequirements requirements = MarketDataRequirements.builder().addValues(id1, id2).build();
BuiltMarketData marketData = factory.create(requirements, MARKET_DATA_CONFIG, suppliedData, REF_DATA);
assertThat(marketData.getValue(id1)).isEqualTo(1d);
assertThat(marketData.getValue(id2)).isEqualTo(2d);
}
/**
* Tests observable market data values supplied by the user are included in the results.
*/
public void buildSuppliedObservableValues() {
MarketDataFactory factory = MarketDataFactory.of(
ObservableDataProvider.none(),
new TestTimeSeriesProvider(ImmutableMap.of()));
TestObservableId id1 = TestObservableId.of("a");
TestObservableId id2 = TestObservableId.of("b");
MarketData suppliedData = ImmutableMarketData.builder(date(2011, 3, 8))
.addValue(id1, 1d)
.addValue(id2, 2d)
.build();
MarketDataRequirements requirements = MarketDataRequirements.builder().addValues(id1, id2).build();
BuiltMarketData marketData = factory.create(requirements, MARKET_DATA_CONFIG, suppliedData, REF_DATA);
assertThat(marketData.getValue(id1)).isEqualTo(1d);
assertThat(marketData.getValue(id2)).isEqualTo(2d);
}
/**
* Tests building market data that depends on other market data.
*/
public void buildDataFromOtherData() {
TestMarketDataFunctionB builderB = new TestMarketDataFunctionB();
TestMarketDataFunctionC builderC = new TestMarketDataFunctionC();
MarketDataRequirements requirements = MarketDataRequirements.builder()
.addValues(new TestIdB("1"), new TestIdB("2"))
.build();
LocalDateDoubleTimeSeries timeSeries1 =
LocalDateDoubleTimeSeries.builder()
.put(date(2011, 3, 8), 1)
.put(date(2011, 3, 9), 2)
.put(date(2011, 3, 10), 3)
.build();
LocalDateDoubleTimeSeries timeSeries2 =
LocalDateDoubleTimeSeries.builder()
.put(date(2011, 3, 8), 10)
.put(date(2011, 3, 9), 20)
.put(date(2011, 3, 10), 30)
.build();
Map<TestIdA, LocalDateDoubleTimeSeries> timeSeriesMap =
ImmutableMap.of(
new TestIdA("1"), timeSeries1,
new TestIdA("2"), timeSeries2);
TimeSeriesProvider timeSeriesProvider = new TestTimeSeriesProvider(timeSeriesMap);
MarketDataFactory factory = MarketDataFactory.of(
new TestObservableDataProvider(),
timeSeriesProvider,
builderB,
builderC);
MarketData suppliedData = MarketData.empty(date(2011, 3, 8));
BuiltMarketData marketData = factory.create(requirements, MARKET_DATA_CONFIG, suppliedData, REF_DATA);
assertThat(marketData.getValueFailures()).isEmpty();
assertThat(marketData.getTimeSeriesFailures()).isEmpty();
TestMarketDataB marketDataB1 = marketData.getValue(new TestIdB("1"));
TestMarketDataB marketDataB2 = marketData.getValue(new TestIdB("2"));
TestMarketDataB expectedB1 = new TestMarketDataB(1, new TestMarketDataC(timeSeries1));
TestMarketDataB expectedB2 = new TestMarketDataB(2, new TestMarketDataC(timeSeries2));
assertThat(marketDataB1).isEqualTo(expectedB1);
assertThat(marketDataB2).isEqualTo(expectedB2);
}
/**
* Tests building market data that depends on other market data that is supplied by the user.
*
* This tests that supplied data is included in scenario data if it is not in the requirements but it is
* needed to build data that is in the requirements.
*
* For example, par rates are required to build curves but are not used directly by functions so the
* requirements will not contain par rates IDs. The requirements contain curve IDs and the curve
* building function will declare that it requires par rates.
*/
public void buildDataFromSuppliedData() {
TestMarketDataFunctionB builderB = new TestMarketDataFunctionB();
TestMarketDataFunctionC builderC = new TestMarketDataFunctionC();
MarketDataRequirements requirements = MarketDataRequirements.builder()
.addValues(new TestIdB("1"), new TestIdB("2"))
.build();
LocalDateDoubleTimeSeries timeSeries1 =
LocalDateDoubleTimeSeries.builder()
.put(date(2011, 3, 8), 1)
.put(date(2011, 3, 9), 2)
.put(date(2011, 3, 10), 3)
.build();
LocalDateDoubleTimeSeries timeSeries2 =
LocalDateDoubleTimeSeries.builder()
.put(date(2011, 3, 8), 10)
.put(date(2011, 3, 9), 20)
.put(date(2011, 3, 10), 30)
.build();
TestIdA idA1 = new TestIdA("1");
TestIdA idA2 = new TestIdA("2");
MarketData suppliedData = ImmutableMarketData.builder(date(2011, 3, 8))
.addTimeSeries(idA1, timeSeries1)
.addTimeSeries(idA2, timeSeries2)
.addValue(idA1, 1d)
.addValue(idA2, 2d)
.build();
MarketDataFactory factory = MarketDataFactory.of(
ObservableDataProvider.none(),
TimeSeriesProvider.none(),
builderB,
builderC);
BuiltMarketData marketData = factory.create(requirements, MARKET_DATA_CONFIG, suppliedData, REF_DATA);
assertThat(marketData.getValueFailures()).isEmpty();
assertThat(marketData.getTimeSeriesFailures()).isEmpty();
TestMarketDataB marketDataB1 = marketData.getValue(new TestIdB("1"));
TestMarketDataB marketDataB2 = marketData.getValue(new TestIdB("2"));
TestMarketDataB expectedB1 = new TestMarketDataB(1, new TestMarketDataC(timeSeries1));
TestMarketDataB expectedB2 = new TestMarketDataB(2, new TestMarketDataC(timeSeries2));
assertThat(marketDataB1).isEqualTo(expectedB1);
assertThat(marketDataB2).isEqualTo(expectedB2);
}
/**
* Tests an exception is thrown when there is no builder for an ID type.
*/
public void noMarketDataBuilderAvailable() {
TestIdB idB1 = new TestIdB("1");
TestIdB idB2 = new TestIdB("2");
TestMarketDataFunctionB builder = new TestMarketDataFunctionB();
// Market data B depends on market data C so these requirements should cause instances of C to be built.
// There is no market data function for building instances of C so this should cause failures.
MarketDataRequirements requirements = MarketDataRequirements.builder()
.addValues(idB1, idB2)
.build();
MarketDataFactory factory = MarketDataFactory.of(
new TestObservableDataProvider(),
new TestTimeSeriesProvider(ImmutableMap.of()),
builder);
BuiltScenarioMarketData suppliedData = BuiltScenarioMarketData.builder(date(2011, 3, 8)).build();
assertThrows(
() -> factory.createMultiScenario(requirements, MARKET_DATA_CONFIG, suppliedData, REF_DATA, ScenarioDefinition.empty()),
IllegalStateException.class,
"No market data function available for market data ID of type.*");
}
/**
* Tests building a result and keeping the intermediate values.
*/
public void buildWithIntermediateValues() {
TestMarketDataFunctionB builderB = new TestMarketDataFunctionB();
TestMarketDataFunctionC builderC = new TestMarketDataFunctionC();
MarketDataRequirements requirements = MarketDataRequirements.builder()
.addValues(new TestIdB("1"), new TestIdB("2"))
.build();
LocalDateDoubleTimeSeries timeSeries1 = LocalDateDoubleTimeSeries.builder()
.put(date(2011, 3, 8), 1)
.put(date(2011, 3, 9), 2)
.put(date(2011, 3, 10), 3)
.build();
LocalDateDoubleTimeSeries timeSeries2 = LocalDateDoubleTimeSeries.builder()
.put(date(2011, 3, 8), 10)
.put(date(2011, 3, 9), 20)
.put(date(2011, 3, 10), 30)
.build();
Map<TestIdA, LocalDateDoubleTimeSeries> timeSeriesMap = ImmutableMap.of(
new TestIdA("1"), timeSeries1,
new TestIdA("2"), timeSeries2);
TimeSeriesProvider timeSeriesProvider = new TestTimeSeriesProvider(timeSeriesMap);
MarketDataFactory factory = MarketDataFactory.of(
new TestObservableDataProvider(),
timeSeriesProvider,
builderB,
builderC);
MarketData suppliedData = MarketData.empty(date(2011, 3, 8));
BuiltMarketData marketData = factory.create(requirements, MARKET_DATA_CONFIG, suppliedData, REF_DATA);
assertThat(marketData.getValueFailures()).isEmpty();
assertThat(marketData.getTimeSeriesFailures()).isEmpty();
TestMarketDataC expectedC1 = new TestMarketDataC(timeSeries1);
TestMarketDataC expectedC2 = new TestMarketDataC(timeSeries2);
TestMarketDataB expectedB1 = new TestMarketDataB(1, expectedC1);
TestMarketDataB expectedB2 = new TestMarketDataB(2, expectedC2);
// Check the values in the requirements are present
assertThat(marketData.getValue(new TestIdB("1"))).isEqualTo(expectedB1);
assertThat(marketData.getValue(new TestIdB("2"))).isEqualTo(expectedB2);
// Check the intermediate values are present
assertThat(marketData.getValue(new TestIdA("1"))).isEqualTo(1d);
assertThat(marketData.getValue(new TestIdA("2"))).isEqualTo(2d);
assertThat(marketData.getValue(new TestIdC("1"))).isEqualTo(expectedC1);
assertThat(marketData.getValue(new TestIdC("2"))).isEqualTo(expectedC2);
}
/**
* Tests building multiple observable values for scenarios where the values aren't perturbed.
*/
public void buildObservableScenarioValues() {
MarketDataFactory factory = MarketDataFactory.of(
new TestObservableDataProvider(),
new TestTimeSeriesProvider(ImmutableMap.of()));
BuiltScenarioMarketData suppliedData = BuiltScenarioMarketData.builder(date(2011, 3, 8)).build();
TestObservableId id1 = TestObservableId.of(StandardId.of("reqs", "a"));
TestObservableId id2 = TestObservableId.of(StandardId.of("reqs", "b"));
MarketDataRequirements requirements = MarketDataRequirements.builder().addValues(id1, id2).build();
// This mapping doesn't perturb any data but it causes three scenarios to be built
PerturbationMapping<Double> mapping =
PerturbationMapping.of(
Double.class,
new FalseFilter<>(TestObservableId.class),
new AbsoluteDoubleShift(1, 2, 3));
ScenarioDefinition scenarioDefinition = ScenarioDefinition.ofMappings(ImmutableList.of(mapping));
BuiltScenarioMarketData marketData = factory.createMultiScenario(
requirements,
MARKET_DATA_CONFIG,
suppliedData,
REF_DATA, scenarioDefinition);
assertThat(marketData.getValue(id1)).isEqualTo(MarketDataBox.ofSingleValue(1d));
assertThat(marketData.getValue(id2)).isEqualTo(MarketDataBox.ofSingleValue(2d));
}
/**
* Tests observable values supplied by the user are included in the results when they aren't perturbed
*/
public void buildSuppliedObservableScenarioValues() {
MarketDataFactory factory = MarketDataFactory.of(
ObservableDataProvider.none(),
new TestTimeSeriesProvider(ImmutableMap.of()));
TestObservableId id1 = TestObservableId.of(StandardId.of("reqs", "a"));
TestObservableId id2 = TestObservableId.of(StandardId.of("reqs", "b"));
BuiltScenarioMarketData suppliedData =
BuiltScenarioMarketData.builder(date(2011, 3, 8))
.addValue(id1, 1d)
.addValue(id2, 2d)
.build();
MarketDataRequirements requirements = MarketDataRequirements.builder().addValues(id1, id2).build();
// This mapping doesn't perturb any data but it causes three scenarios to be built
PerturbationMapping<Double> mapping =
PerturbationMapping.of(
Double.class,
new FalseFilter<>(TestObservableId.class),
new AbsoluteDoubleShift(1, 2, 3));
ScenarioDefinition scenarioDefinition = ScenarioDefinition.ofMappings(ImmutableList.of(mapping));
BuiltScenarioMarketData marketData = factory.createMultiScenario(
requirements,
MARKET_DATA_CONFIG,
suppliedData,
REF_DATA, scenarioDefinition);
assertThat(marketData.getValue(id1)).isEqualTo(MarketDataBox.ofSingleValue(1d));
assertThat(marketData.getValue(id2)).isEqualTo(MarketDataBox.ofSingleValue(2d));
}
/**
* Test that time series from the supplied data are copied to the scenario data.
*/
public void buildSuppliedTimeSeries() {
MarketDataFactory factory = MarketDataFactory.of(
ObservableDataProvider.none(),
new TestTimeSeriesProvider(ImmutableMap.of()));
TestObservableId id1 = TestObservableId.of(StandardId.of("reqs", "a"));
TestObservableId id2 = TestObservableId.of(StandardId.of("reqs", "b"));
LocalDateDoubleTimeSeries timeSeries1 = LocalDateDoubleTimeSeries.builder()
.put(date(2011, 3, 8), 1)
.put(date(2011, 3, 9), 2)
.put(date(2011, 3, 10), 3)
.build();
LocalDateDoubleTimeSeries timeSeries2 = LocalDateDoubleTimeSeries.builder()
.put(date(2011, 3, 8), 10)
.put(date(2011, 3, 9), 20)
.put(date(2011, 3, 10), 30)
.build();
BuiltScenarioMarketData suppliedData = BuiltScenarioMarketData.builder(date(2011, 3, 8))
.addTimeSeries(id1, timeSeries1)
.addTimeSeries(id2, timeSeries2)
.build();
MarketDataRequirements requirements = MarketDataRequirements.builder().addTimeSeries(id1, id2).build();
// This mapping doesn't perturb any data but it causes three scenarios to be built
PerturbationMapping<Double> mapping = PerturbationMapping.of(
Double.class,
new FalseFilter<>(TestObservableId.class),
new AbsoluteDoubleShift(1, 2, 3));
ScenarioDefinition scenarioDefinition = ScenarioDefinition.ofMappings(ImmutableList.of(mapping));
BuiltScenarioMarketData marketData = factory.createMultiScenario(
requirements,
MARKET_DATA_CONFIG,
suppliedData,
REF_DATA, scenarioDefinition);
assertThat(marketData.getTimeSeries(id1)).isEqualTo(timeSeries1);
assertThat(marketData.getTimeSeries(id2)).isEqualTo(timeSeries2);
}
public void perturbObservableValues() {
MarketDataFactory factory = MarketDataFactory.of(
new TestObservableDataProvider(),
new TestTimeSeriesProvider(ImmutableMap.of()));
BuiltScenarioMarketData suppliedData = BuiltScenarioMarketData.builder(date(2011, 3, 8)).build();
TestObservableId id1 = TestObservableId.of(StandardId.of("reqs", "a"));
TestObservableId id2 = TestObservableId.of(StandardId.of("reqs", "b"));
MarketDataRequirements requirements = MarketDataRequirements.builder().addValues(id1, id2).build();
PerturbationMapping<Double> mapping = PerturbationMapping.of(
Double.class,
new ExactIdFilter<>(id1),
new AbsoluteDoubleShift(1, 2, 3));
ScenarioDefinition scenarioDefinition = ScenarioDefinition.ofMappings(ImmutableList.of(mapping));
BuiltScenarioMarketData marketData = factory.createMultiScenario(
requirements,
MARKET_DATA_CONFIG,
suppliedData,
REF_DATA, scenarioDefinition);
assertThat(marketData.getValue(id1)).isEqualTo(MarketDataBox.ofScenarioValues(2d, 3d, 4d));
assertThat(marketData.getValue(id2)).isEqualTo(MarketDataBox.ofSingleValue(2d));
}
/**
* Tests that observable data is only perturbed once, even if there are two applicable perturbation mappings.
*/
public void observableDataOnlyPerturbedOnce() {
MarketDataFactory factory = MarketDataFactory.of(
new TestObservableDataProvider(),
new TestTimeSeriesProvider(ImmutableMap.of()));
BuiltScenarioMarketData suppliedData = BuiltScenarioMarketData.builder(date(2011, 3, 8)).build();
TestObservableId id1 = TestObservableId.of(StandardId.of("reqs", "a"));
TestObservableId id2 = TestObservableId.of(StandardId.of("reqs", "b"));
MarketDataRequirements requirements = MarketDataRequirements.builder().addValues(id1, id2).build();
PerturbationMapping<Double> mapping1 = PerturbationMapping.of(
Double.class,
new ExactIdFilter<>(id2),
new RelativeDoubleShift(0.1, 0.2, 0.3));
PerturbationMapping<Double> mapping2 = PerturbationMapping.of(
Double.class,
new ExactIdFilter<>(id2),
new AbsoluteDoubleShift(1, 2, 3));
ScenarioDefinition scenarioDefinition = ScenarioDefinition.ofMappings(ImmutableList.of(mapping1, mapping2));
BuiltScenarioMarketData marketData = factory.createMultiScenario(
requirements,
MARKET_DATA_CONFIG,
suppliedData,
REF_DATA, scenarioDefinition);
assertThat(marketData.getValue(id1)).isEqualTo(MarketDataBox.ofSingleValue(1d));
assertThat(marketData.getValue(id2)).isEqualTo(MarketDataBox.ofScenarioValues(2.2d, 2.4d, 2.6d));
}
/**
* Tests building multiple values of non-observable market data for multiple scenarios. The data isn't perturbed.
*/
public void buildNonObservableScenarioValues() {
MarketDataFactory factory = MarketDataFactory.of(
new TestObservableDataProvider(),
new TestTimeSeriesProvider(ImmutableMap.of()),
new NonObservableMarketDataFunction());
BuiltScenarioMarketData suppliedData = BuiltScenarioMarketData.builder(date(2011, 3, 8)).build();
NonObservableId id1 = new NonObservableId("a");
NonObservableId id2 = new NonObservableId("b");
MarketDataRequirements requirements = MarketDataRequirements.builder().addValues(id1, id2).build();
// This mapping doesn't perturb any data but it causes three scenarios to be built
PerturbationMapping<String> mapping = PerturbationMapping.of(
String.class,
new FalseFilter<>(NonObservableId.class),
new StringAppender("", "", ""));
ScenarioDefinition scenarioDefinition = ScenarioDefinition.ofMappings(ImmutableList.of(mapping));
BuiltScenarioMarketData marketData = factory.createMultiScenario(
requirements,
MARKET_DATA_CONFIG,
suppliedData,
REF_DATA, scenarioDefinition);
MarketDataBox<String> box1 = marketData.getValue(id1);
assertThat(box1.getValue(0)).isEqualTo("1.0");
assertThat(box1.getValue(1)).isEqualTo("1.0");
assertThat(box1.getValue(2)).isEqualTo("1.0");
MarketDataBox<String> box2 = marketData.getValue(id2);
assertThat(box2.getValue(0)).isEqualTo("2.0");
assertThat(box2.getValue(1)).isEqualTo("2.0");
assertThat(box2.getValue(2)).isEqualTo("2.0");
}
/**
* Tests non-observable values supplied by the user are included in the results when they aren't perturbed
*/
public void buildSuppliedNonObservableScenarioValues() {
MarketDataFactory factory = MarketDataFactory.of(
ObservableDataProvider.none(),
new TestTimeSeriesProvider(ImmutableMap.of()));
NonObservableId id1 = new NonObservableId("a");
NonObservableId id2 = new NonObservableId("b");
BuiltScenarioMarketData suppliedData = BuiltScenarioMarketData.builder(date(2011, 3, 8))
.addValue(id1, "value1")
.addValue(id2, "value2")
.build();
MarketDataRequirements requirements = MarketDataRequirements.builder().addValues(id1, id2).build();
// This mapping doesn't perturb any data but it causes three scenarios to be built
PerturbationMapping<String> mapping = PerturbationMapping.of(
String.class,
new FalseFilter<>(NonObservableId.class),
new StringAppender("", "", ""));
ScenarioDefinition scenarioDefinition = ScenarioDefinition.ofMappings(ImmutableList.of(mapping));
BuiltScenarioMarketData marketData = factory.createMultiScenario(
requirements,
MARKET_DATA_CONFIG,
suppliedData,
REF_DATA, scenarioDefinition);
assertThat(marketData.getValue(id1)).isEqualTo(MarketDataBox.ofSingleValue("value1"));
assertThat(marketData.getValue(id2)).isEqualTo(MarketDataBox.ofSingleValue("value2"));
}
/**
* Tests building scenario data from values that are supplied by the user but aren't directly required
* by the functions.
*
* For example, par rates are required to build curves but are not used directly by functions so the
* requirements will not contain par rates IDs. The requirements contain curve IDs and the curve
* building function will declare that it requires par rates.
*/
public void buildScenarioValuesFromSuppliedData() {
TestMarketDataFunctionB builderB = new TestMarketDataFunctionB();
TestMarketDataFunctionC builderC = new TestMarketDataFunctionC();
TestIdB idB1 = new TestIdB("1");
TestIdB idB2 = new TestIdB("2");
MarketDataRequirements requirements = MarketDataRequirements.builder()
.addValues(idB1, idB2)
.build();
LocalDateDoubleTimeSeries timeSeries1 = LocalDateDoubleTimeSeries.builder()
.put(date(2011, 3, 8), 1)
.put(date(2011, 3, 9), 2)
.put(date(2011, 3, 10), 3)
.build();
LocalDateDoubleTimeSeries timeSeries2 = LocalDateDoubleTimeSeries.builder()
.put(date(2011, 3, 8), 10)
.put(date(2011, 3, 9), 20)
.put(date(2011, 3, 10), 30)
.build();
TestIdA idA1 = new TestIdA("1");
TestIdA idA2 = new TestIdA("2");
BuiltScenarioMarketData suppliedData = BuiltScenarioMarketData.builder(date(2011, 3, 8))
.addTimeSeries(idA1, timeSeries1)
.addTimeSeries(idA2, timeSeries2)
.addValue(idA1, 1d)
.addValue(idA2, 2d)
.build();
MarketDataFactory marketDataFactory = MarketDataFactory.of(
ObservableDataProvider.none(),
TimeSeriesProvider.none(),
builderB,
builderC);
PerturbationMapping<Double> aMapping = PerturbationMapping.of(
Double.class,
new ExactIdFilter<>(new TestIdA("2")),
new RelativeDoubleShift(0.2, 0.3, 0.4));
PerturbationMapping<TestMarketDataC> cMapping = PerturbationMapping.of(
TestMarketDataC.class,
new ExactIdFilter<>(new TestIdC("1")),
new TestCPerturbation(1.1, 1.2, 1.3));
ScenarioDefinition scenarioDefinition = ScenarioDefinition.ofMappings(aMapping, cMapping);
BuiltScenarioMarketData marketData = marketDataFactory.createMultiScenario(
requirements,
MARKET_DATA_CONFIG,
suppliedData,
REF_DATA, scenarioDefinition);
assertThat(marketData.getValueFailures()).isEmpty();
assertThat(marketData.getTimeSeriesFailures()).isEmpty();
MarketDataBox<TestMarketDataB> marketDataB1 = marketData.getValue(idB1);
MarketDataBox<TestMarketDataB> marketDataB2 = marketData.getValue(idB2);
MarketDataBox<TestMarketDataB> expectedB1 = MarketDataBox.ofScenarioValues(
new TestMarketDataB(1, new TestMarketDataC(timeSeries1.mapValues(v -> v * 1.1))),
new TestMarketDataB(1, new TestMarketDataC(timeSeries1.mapValues(v -> v * 1.2))),
new TestMarketDataB(1, new TestMarketDataC(timeSeries1.mapValues(v -> v * 1.3))));
MarketDataBox<TestMarketDataB> expectedB2 = MarketDataBox.ofScenarioValues(
new TestMarketDataB(2.4, new TestMarketDataC(timeSeries2)),
new TestMarketDataB(2.6, new TestMarketDataC(timeSeries2)),
new TestMarketDataB(2.8, new TestMarketDataC(timeSeries2)));
assertThat(marketDataB1).isEqualTo(expectedB1);
assertThat(marketDataB2).isEqualTo(expectedB2);
}
/**
* Tests that perturbations are applied to non-observable market data.
*/
public void perturbNonObservableValues() {
MarketDataFactory factory = MarketDataFactory.of(
new TestObservableDataProvider(),
new TestTimeSeriesProvider(ImmutableMap.of()),
new NonObservableMarketDataFunction());
BuiltScenarioMarketData suppliedData = BuiltScenarioMarketData.builder(date(2011, 3, 8)).build();
NonObservableId id1 = new NonObservableId("a");
NonObservableId id2 = new NonObservableId("b");
MarketDataRequirements requirements = MarketDataRequirements.builder().addValues(id1, id2).build();
PerturbationMapping<String> mapping =
PerturbationMapping.of(
String.class,
new ExactIdFilter<>(id1),
new StringAppender("foo", "bar", "baz"));
ScenarioDefinition scenarioDefinition = ScenarioDefinition.ofMappings(ImmutableList.of(mapping));
BuiltScenarioMarketData marketData = factory.createMultiScenario(
requirements,
MARKET_DATA_CONFIG,
suppliedData,
REF_DATA, scenarioDefinition);
assertThat(marketData.getValue(id1)).isEqualTo(MarketDataBox.ofScenarioValues("1.0foo", "1.0bar", "1.0baz"));
assertThat(marketData.getValue(id2)).isEqualTo(MarketDataBox.ofSingleValue("2.0"));
}
/**
* Tests that non-observable data is only perturbed once, even if there are two applicable perturbation mappings.
*/
public void nonObservableDataOnlyPerturbedOnce() {
MarketDataFactory factory = MarketDataFactory.of(
new TestObservableDataProvider(),
new TestTimeSeriesProvider(ImmutableMap.of()),
new NonObservableMarketDataFunction());
BuiltScenarioMarketData suppliedData = BuiltScenarioMarketData.builder(date(2011, 3, 8)).build();
NonObservableId id1 = new NonObservableId("a");
NonObservableId id2 = new NonObservableId("b");
MarketDataRequirements requirements = MarketDataRequirements.builder().addValues(id1, id2).build();
PerturbationMapping<String> mapping1 = PerturbationMapping.of(
String.class,
new ExactIdFilter<>(id1),
new StringAppender("FOO", "BAR", "BAZ"));
PerturbationMapping<String> mapping2 = PerturbationMapping.of(
String.class,
new ExactIdFilter<>(id1),
new StringAppender("foo", "bar", "baz"));
ScenarioDefinition scenarioDefinition = ScenarioDefinition.ofMappings(ImmutableList.of(mapping1, mapping2));
BuiltScenarioMarketData marketData = factory.createMultiScenario(
requirements,
MARKET_DATA_CONFIG,
suppliedData,
REF_DATA, scenarioDefinition);
assertThat(marketData.getValue(id1)).isEqualTo(MarketDataBox.ofScenarioValues("1.0FOO", "1.0BAR", "1.0BAZ"));
assertThat(marketData.getValue(id2)).isEqualTo(MarketDataBox.ofSingleValue("2.0"));
}
/**
* Tests that observable data built from observable values see the effects of the perturbations.
*/
public void nonObservableDataBuiltFromPerturbedObservableData() {
MarketDataFactory factory = MarketDataFactory.of(
new TestObservableDataProvider(),
new TestTimeSeriesProvider(ImmutableMap.of()),
new NonObservableMarketDataFunction());
BuiltScenarioMarketData suppliedData = BuiltScenarioMarketData.builder(date(2011, 3, 8)).build();
MarketDataId<?> id1 = new NonObservableId("a");
MarketDataId<?> id2 = new NonObservableId("b");
TestObservableId quoteId = TestObservableId.of(StandardId.of("reqs", "b"));
MarketDataRequirements requirements = MarketDataRequirements.builder().addValues(id1, id2).build();
PerturbationMapping<Double> mapping = PerturbationMapping.of(
Double.class,
new ExactIdFilter<>(quoteId),
new RelativeDoubleShift(0.1, 0.2, 0.3));
ScenarioDefinition scenarioDefinition = ScenarioDefinition.ofMappings(ImmutableList.of(mapping));
BuiltScenarioMarketData marketData = factory.createMultiScenario(
requirements,
MARKET_DATA_CONFIG,
suppliedData,
REF_DATA, scenarioDefinition);
assertThat(marketData.getValue(id1)).isEqualTo(MarketDataBox.ofSingleValue("1.0"));
assertThat(marketData.getValue(id2)).isEqualTo(MarketDataBox.ofScenarioValues("2.2", "2.4", "2.6"));
}
/**
* Tests that an exception is thrown when building observable market data for scenarios where there is no
* market data function.
*/
public void nonObservableScenarioDataWithMissingBuilder() {
MarketDataFactory factory = MarketDataFactory.of(
new TestObservableDataProvider(),
new TestTimeSeriesProvider(ImmutableMap.of()));
BuiltScenarioMarketData suppliedData = BuiltScenarioMarketData.builder(date(2011, 3, 8)).build();
NonObservableId id1 = new NonObservableId("a");
NonObservableId id2 = new NonObservableId("b");
MarketDataRequirements requirements = MarketDataRequirements.builder().addValues(id1, id2).build();
// This mapping doesn't perturb any data but it causes three scenarios to be built
PerturbationMapping<String> mapping = PerturbationMapping.of(
String.class,
new FalseFilter<>(NonObservableId.class),
new StringAppender("", "", ""));
ScenarioDefinition scenarioDefinition = ScenarioDefinition.ofMappings(ImmutableList.of(mapping));
assertThrows(
() -> factory.createMultiScenario(
requirements,
MARKET_DATA_CONFIG,
suppliedData,
REF_DATA, scenarioDefinition),
IllegalStateException.class,
"No market data function available for market data ID of type.*");
}
/**
* Tests that perturbations are applied to observable data supplied by the user.
*/
public void perturbSuppliedNonObservableData() {
MarketDataFactory factory = MarketDataFactory.of(
ObservableDataProvider.none(),
new TestTimeSeriesProvider(ImmutableMap.of()));
NonObservableId id = new NonObservableId("a");
PerturbationMapping<String> mapping = PerturbationMapping.of(
String.class,
new ExactIdFilter<>(id),
new StringAppender("Foo", "Bar", "Baz"));
ScenarioDefinition scenarioDefinition = ScenarioDefinition.ofMappings(ImmutableList.of(mapping));
BuiltScenarioMarketData suppliedData = BuiltScenarioMarketData.builder(date(2011, 3, 8))
.addValue(id, "value")
.build();
MarketDataRequirements requirements = MarketDataRequirements.builder().addValues(id).build();
BuiltScenarioMarketData marketData = factory.createMultiScenario(
requirements,
MARKET_DATA_CONFIG,
suppliedData,
REF_DATA, scenarioDefinition);
MarketDataBox<String> values = marketData.getValue(id);
MarketDataBox<String> expectedValues = MarketDataBox.ofScenarioValues("valueFoo", "valueBar", "valueBaz");
assertThat(values).isEqualTo(expectedValues);
}
/**
* Tests that perturbations are applied to non-observable data supplied by the user.
*/
public void perturbSuppliedObservableData() {
MarketDataFactory factory = MarketDataFactory.of(
ObservableDataProvider.none(),
new TestTimeSeriesProvider(ImmutableMap.of()));
TestObservableId id = TestObservableId.of(StandardId.of("reqs", "a"));
MarketDataRequirements requirements = MarketDataRequirements.builder().addValues(id).build();
PerturbationMapping<Double> mapping = PerturbationMapping.of(
Double.class,
new ExactIdFilter<>(id),
new RelativeDoubleShift(0.1, 0.2, 0.3));
ScenarioDefinition scenarioDefinition = ScenarioDefinition.ofMappings(ImmutableList.of(mapping));
BuiltScenarioMarketData suppliedData = BuiltScenarioMarketData.builder(date(2011, 3, 8))
.addValue(id, 2d)
.build();
BuiltScenarioMarketData marketData = factory.createMultiScenario(
requirements,
MARKET_DATA_CONFIG,
suppliedData,
REF_DATA, scenarioDefinition);
MarketDataBox<Double> values = marketData.getValue(id);
MarketDataBox<Double> expectedValues = MarketDataBox.ofScenarioValues(2.2, 2.4, 2.6);
assertThat(values).isEqualTo(expectedValues);
}
/**
* Tests ObservableDataProvider.none(), which is never normally be invoked.
*/
public void coverage_ObservableDataProvider_none() {
TestObservableId id = TestObservableId.of(StandardId.of("reqs", "a"));
ObservableDataProvider test = ObservableDataProvider.none();
Map<ObservableId, Result<Double>> result = test.provideObservableData(ImmutableSet.of(id));
assertThat(result).containsOnlyKeys(id);
assertThat(result.get(id).isFailure()).isTrue();
}
//-------------------------------------------------------------------------
/**
* Simple time series provider backed by a map.
*/
private static final class TestTimeSeriesProvider implements TimeSeriesProvider {
private final Map<? extends ObservableId, LocalDateDoubleTimeSeries> timeSeries;
private TestTimeSeriesProvider(Map<? extends ObservableId, LocalDateDoubleTimeSeries> timeSeries) {
this.timeSeries = timeSeries;
}
@Override
public Result<LocalDateDoubleTimeSeries> provideTimeSeries(ObservableId id) {
LocalDateDoubleTimeSeries series = timeSeries.get(id);
return Result.ofNullable(series, FailureReason.MISSING_DATA, "No time series found for ID {}", id);
}
}
/**
* Builds observable data by parsing the value of the standard ID.
*/
private static final class TestObservableDataProvider implements ObservableDataProvider {
// demonstrates provider that maps identifiers
private final Map<ObservableId, ObservableId> idMap =
ImmutableMap.of(
TestObservableId.of(StandardId.of("reqs", "a")), TestObservableId.of(StandardId.of("vendor", "1")),
TestObservableId.of(StandardId.of("reqs", "b")), TestObservableId.of(StandardId.of("vendor", "2")));
@Override
public Map<ObservableId, Result<Double>> provideObservableData(Set<? extends ObservableId> requirements) {
return requirements.stream().collect(toImmutableMap(id -> id, id -> buildResult(idMap.getOrDefault(id, id))));
}
private Result<Double> buildResult(ObservableId id) {
return Result.success(Double.parseDouble(id.getStandardId().getValue()));
}
}
//-------------------------------------------------------------------------
/**
* Test ID A.
*/
private static final class TestIdA implements ObservableId {
private final StandardId id;
TestIdA(String id) {
this.id = StandardId.of("test", id);
}
@Override
public StandardId getStandardId() {
return id;
}
@Override
public FieldName getFieldName() {
return FieldName.MARKET_VALUE;
}
@Override
public ObservableSource getObservableSource() {
return ObservableSource.NONE;
}
@Override
public ObservableId withObservableSource(ObservableSource obsSource) {
return this;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TestIdA id = (TestIdA) o;
return Objects.equals(this.id, id.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public String toString() {
return "TestIdA [id=" + id + "]";
}
}
/**
* Test ID B.
*/
private static final class TestIdB implements MarketDataId<TestMarketDataB> {
private final String str;
TestIdB(String str) {
this.str = str;
}
@Override
public Class<TestMarketDataB> getMarketDataType() {
return TestMarketDataB.class;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TestIdB id = (TestIdB) o;
return Objects.equals(str, id.str);
}
@Override
public int hashCode() {
return Objects.hash(str);
}
@Override
public String toString() {
return "TestIdB [str='" + str + "']";
}
}
private static final class TestIdC implements MarketDataId<TestMarketDataC> {
private final String str;
TestIdC(String str) {
this.str = str;
}
@Override
public Class<TestMarketDataC> getMarketDataType() {
return TestMarketDataC.class;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TestIdC id = (TestIdC) o;
return Objects.equals(str, id.str);
}
@Override
public int hashCode() {
return Objects.hash(str);
}
@Override
public String toString() {
return "TestIdC [str='" + str + "']";
}
}
/**
* Test market data B.
*/
private static final class TestMarketDataB {
private final double value;
private final TestMarketDataC marketData;
TestMarketDataB(double value, TestMarketDataC marketData) {
this.value = value;
this.marketData = marketData;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TestMarketDataB that = (TestMarketDataB) o;
return Objects.equals(value, that.value) &&
Objects.equals(marketData, that.marketData);
}
@Override
public int hashCode() {
return Objects.hash(value, marketData);
}
}
/**
* Function for building TestMarketDataB.
* Requires a value with ID TestIdA(id.str) and TestMarketDataC with ID TestIdC(id.str)
*/
private final class TestMarketDataFunctionB implements MarketDataFunction<TestMarketDataB, TestIdB> {
@Override
public MarketDataRequirements requirements(TestIdB id, MarketDataConfig marketDataConfig) {
return MarketDataRequirements.builder()
.addValues(new TestIdA(id.str), new TestIdC(id.str))
.build();
}
@Override
public MarketDataBox<TestMarketDataB> build(
TestIdB id,
MarketDataConfig marketDataConfig,
ScenarioMarketData marketData,
ReferenceData refData) {
TestIdA idA = new TestIdA(id.str);
TestIdC idC = new TestIdC(id.str);
MarketDataBox<Double> valueA = marketData.getValue(idA);
MarketDataBox<TestMarketDataC> marketDataC = marketData.getValue(idC);
return valueA.combineWith(marketDataC, TestMarketDataB::new);
}
@Override
public Class<TestIdB> getMarketDataIdType() {
return TestIdB.class;
}
}
/**
* Test market data C.
*/
private static final class TestMarketDataC {
private final LocalDateDoubleTimeSeries timeSeries;
TestMarketDataC(LocalDateDoubleTimeSeries timeSeries) {
this.timeSeries = timeSeries;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TestMarketDataC that = (TestMarketDataC) o;
return Objects.equals(timeSeries, that.timeSeries);
}
@Override
public int hashCode() {
return Objects.hash(timeSeries);
}
}
/**
* Function for building TestMarketDataC.
* Requires a time series with ID TestIdA(id.str)
*/
private static final class TestMarketDataFunctionC implements MarketDataFunction<TestMarketDataC, TestIdC> {
@Override
public MarketDataRequirements requirements(TestIdC id, MarketDataConfig marketDataConfig) {
return MarketDataRequirements.builder()
.addTimeSeries(new TestIdA(id.str))
.build();
}
@Override
public MarketDataBox<TestMarketDataC> build(
TestIdC id,
MarketDataConfig marketDataConfig,
ScenarioMarketData marketData,
ReferenceData refData) {
LocalDateDoubleTimeSeries timeSeries = marketData.getTimeSeries(new TestIdA(id.str));
return MarketDataBox.ofSingleValue(new TestMarketDataC(timeSeries));
}
@Override
public Class<TestIdC> getMarketDataIdType() {
return TestIdC.class;
}
}
/**
* Market data filter that doesn't match any market data.
*/
private static final class FalseFilter<T, I extends MarketDataId<T>> implements MarketDataFilter<T, I> {
private final Class<?> idType;
private FalseFilter(Class<?> idType) {
this.idType = idType;
}
@Override
public boolean matches(I marketDataId, MarketDataBox<T> marketData, ReferenceData refData) {
return false;
}
@Override
public Class<?> getMarketDataIdType() {
return idType;
}
}
/**
* Perturbation that applies a shift to a double value.
*/
private static final class AbsoluteDoubleShift implements ScenarioPerturbation<Double> {
private final double[] shiftAmount;
private AbsoluteDoubleShift(double... shiftAmount) {
this.shiftAmount = shiftAmount;
}
@Override
public MarketDataBox<Double> applyTo(MarketDataBox<Double> marketData, ReferenceData refData) {
return marketData.mapWithIndex(getScenarioCount(), (value, scenarioIndex) -> value + shiftAmount[scenarioIndex]);
}
@Override
public int getScenarioCount() {
return shiftAmount.length;
}
}
/**
* Perturbation that applies a shift to a double value.
*/
private static final class RelativeDoubleShift implements ScenarioPerturbation<Double> {
private final double[] shiftAmounts;
private RelativeDoubleShift(double... shiftAmounts) {
this.shiftAmounts = shiftAmounts;
}
@Override
public MarketDataBox<Double> applyTo(MarketDataBox<Double> marketData, ReferenceData refData) {
return marketData.mapWithIndex(getScenarioCount(), (value, scenarioIndex) -> value * (1 + shiftAmounts[scenarioIndex]));
}
@Override
public int getScenarioCount() {
return shiftAmounts.length;
}
}
/**
* Market data filter that matches an ID exactly.
*/
private static final class ExactIdFilter<T, I extends MarketDataId<T>> implements MarketDataFilter<T, I> {
private final I id;
private ExactIdFilter(I id) {
this.id = id;
}
@Override
public boolean matches(I marketDataId, MarketDataBox<T> marketData, ReferenceData refData) {
return id.equals(marketDataId);
}
@Override
public Class<?> getMarketDataIdType() {
return id.getClass();
}
}
/**
* Market data ID for a piece of non-observable market data that is a string.
*/
private static final class NonObservableId implements MarketDataId<String> {
private final String str;
private NonObservableId(String str) {
this.str = str;
}
@Override
public Class<String> getMarketDataType() {
return String.class;
}
@Override
public String toString() {
return "NonObservableId [str='" + str + "']";
}
}
/**
* Market data function that builds a piece of non-observable market data (a string).
*/
private static final class NonObservableMarketDataFunction implements MarketDataFunction<String, NonObservableId> {
@Override
public MarketDataRequirements requirements(NonObservableId id, MarketDataConfig marketDataConfig) {
return MarketDataRequirements.builder()
.addValues(TestObservableId.of(StandardId.of("reqs", id.str)))
.build();
}
@Override
public MarketDataBox<String> build(
NonObservableId id,
MarketDataConfig marketDataConfig,
ScenarioMarketData marketData,
ReferenceData refData) {
MarketDataBox<Double> value = marketData.getValue(TestObservableId.of(StandardId.of("reqs", id.str)));
return value.map(v -> Double.toString(v));
}
@Override
public Class<NonObservableId> getMarketDataIdType() {
return NonObservableId.class;
}
}
/**
* A perturbation which perturbs a string by appending another string to it.
*/
private static final class StringAppender implements ScenarioPerturbation<String> {
private final String[] str;
public StringAppender(String... str) {
this.str = str;
}
@Override
public MarketDataBox<String> applyTo(MarketDataBox<String> marketData, ReferenceData refData) {
return marketData.mapWithIndex(getScenarioCount(), (value, scenarioIndex) -> value + str[scenarioIndex]);
}
@Override
public int getScenarioCount() {
return str.length;
}
}
/**
* Perturbation that perturbs TestMarketDataC by scaling its time series.
*/
private static final class TestCPerturbation implements ScenarioPerturbation<TestMarketDataC> {
private final double[] scaleFactors;
private TestCPerturbation(double... scaleFactors) {
this.scaleFactors = scaleFactors;
}
@Override
public MarketDataBox<TestMarketDataC> applyTo(MarketDataBox<TestMarketDataC> marketData, ReferenceData refData) {
return marketData.mapWithIndex(getScenarioCount(), this::perturb);
}
private TestMarketDataC perturb(TestMarketDataC data, int scenarioIndex) {
LocalDateDoubleTimeSeries perturbedTimeSeries = data.timeSeries.mapValues(v -> v * scaleFactors[scenarioIndex]);
return new TestMarketDataC(perturbedTimeSeries);
}
@Override
public int getScenarioCount() {
return scaleFactors.length;
}
}
}