/**
* 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.TestHelper.date;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.basics.StandardId;
import com.opengamma.strata.collect.tuple.Pair;
import com.opengamma.strata.data.FieldName;
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;
@Test
public class MarketDataNodeTest {
/**
* Tests removing the leaf dependencies from the tree to decide which market data can be built.
*/
public void withLeavesRemoved() {
MarketDataNode root =
rootNode(
observableNode(new TestIdA("1")),
valueNode(
new TestIdB("2"),
valueNode(new TestIdB("3")),
observableNode(new TestIdA("4")),
valueNode(
new TestIdB("5"),
timeSeriesNode(new TestIdA("6")))),
valueNode(new TestIdB("7")));
Pair<MarketDataNode, MarketDataRequirements> pair1 = root.withLeavesRemoved();
MarketDataRequirements expectedReqs1 =
MarketDataRequirements.builder()
.addValues(new TestIdA("1"))
.addValues(new TestIdB("3"))
.addValues(new TestIdA("4"))
.addTimeSeries(new TestIdA("6"))
.addValues(new TestIdB("7"))
.build();
MarketDataNode expectedTree1 =
rootNode(
valueNode(
new TestIdB("2"),
valueNode(
new TestIdB("5"))));
MarketDataNode tree1 = pair1.getFirst();
MarketDataRequirements reqs1 = pair1.getSecond();
assertThat(tree1).isEqualTo(expectedTree1);
assertThat(expectedReqs1).isEqualTo(reqs1);
Pair<MarketDataNode, MarketDataRequirements> pair2 = tree1.withLeavesRemoved();
MarketDataRequirements expectedReqs2 =
MarketDataRequirements.builder()
.addValues(new TestIdB("5"))
.build();
MarketDataNode expectedTree2 =
rootNode(
valueNode(
new TestIdB("2")));
MarketDataNode tree2 = pair2.getFirst();
MarketDataRequirements reqs2 = pair2.getSecond();
assertThat(tree2).isEqualTo(expectedTree2);
assertThat(expectedReqs2).isEqualTo(reqs2);
Pair<MarketDataNode, MarketDataRequirements> pair3 = tree2.withLeavesRemoved();
MarketDataRequirements expectedReqs3 =
MarketDataRequirements.builder()
.addValues(new TestIdB("2"))
.build();
MarketDataNode tree3 = pair3.getFirst();
MarketDataRequirements reqs3 = pair3.getSecond();
assertThat(tree3.isLeaf()).isTrue();
assertThat(expectedReqs3).isEqualTo(reqs3);
}
/**
* Tests building a tree of requirements using market data functions.
*/
public void buildDependencyTree() {
MarketDataNode expected =
rootNode(
observableNode(new TestIdA("1")),
valueNode(
new TestIdB("2"),
valueNode(
new TestIdB("4"),
observableNode(new TestIdA("5"))),
timeSeriesNode(new TestIdA("3"))),
timeSeriesNode(new TestIdA("6")));
// The requirements for the data directly used by the calculations
MarketDataRequirements requirements =
MarketDataRequirements.builder()
.addValues(new TestIdA("1"), new TestIdB("2"))
.addTimeSeries(new TestIdA("6"))
.build();
// Requirements for each item in the tree - used to initialize the functions
MarketDataRequirements id2Reqs =
MarketDataRequirements.builder()
.addTimeSeries(new TestIdA("3"))
.addValues(new TestIdB("4"))
.build();
MarketDataRequirements id4Reqs =
MarketDataRequirements.builder()
.addValues(new TestIdA("5"))
.build();
ImmutableMap<TestIdB, MarketDataRequirements> reqsMap =
ImmutableMap.of(
new TestIdB("2"), id2Reqs,
new TestIdB("4"), id4Reqs);
TestMarketDataFunctionA builderA = new TestMarketDataFunctionA();
TestMarketDataFunctionB builderB = new TestMarketDataFunctionB(reqsMap);
ImmutableMap<Class<? extends MarketDataId<?>>, MarketDataFunction<?, ?>> functions =
ImmutableMap.of(
TestIdA.class, builderA,
TestIdB.class, builderB);
MarketDataNode root =
MarketDataNode.buildDependencyTree(
requirements,
BuiltScenarioMarketData.empty(),
MarketDataConfig.empty(),
functions);
assertThat(root).isEqualTo(expected);
}
/**
* Tests that supplied data is in a leaf node and the functions aren't asked for dependencies for supplied data.
*/
public void noDependenciesForSuppliedData() {
MarketDataNode expected1 =
rootNode(
valueNode(
new TestIdB("1"),
observableNode(new TestIdA("2"))),
valueNode(
new TestIdB("3"),
valueNode(new TestIdB("4"))));
MarketDataRequirements requirements =
MarketDataRequirements.builder()
.addValues(new TestIdB("1"), new TestIdB("3"))
.build();
MarketDataRequirements id1Reqs =
MarketDataRequirements.builder()
.addValues(new TestIdA("2"))
.build();
MarketDataRequirements id3Reqs =
MarketDataRequirements.builder()
.addValues(new TestIdB("4"))
.build();
ImmutableMap<TestIdB, MarketDataRequirements> reqsMap =
ImmutableMap.of(
new TestIdB("1"), id1Reqs,
new TestIdB("3"), id3Reqs);
TestMarketDataFunctionB builder = new TestMarketDataFunctionB(reqsMap);
ImmutableMap<Class<? extends MarketDataId<?>>, MarketDataFunction<?, ?>> functions =
ImmutableMap.of(
TestIdB.class, builder);
MarketDataNode root1 =
MarketDataNode.buildDependencyTree(
requirements,
BuiltScenarioMarketData.empty(),
MarketDataConfig.empty(),
functions);
assertThat(root1).isEqualTo(expected1);
BuiltScenarioMarketData suppliedData =
BuiltScenarioMarketData.builder(date(2011, 3, 8))
.addValue(new TestIdB("1"), new TestMarketDataB())
.addValue(new TestIdB("3"), new TestMarketDataB())
.build();
MarketDataNode root2 =
MarketDataNode.buildDependencyTree(
requirements,
suppliedData,
MarketDataConfig.empty(),
functions);
MarketDataNode expected2 =
rootNode(
valueNode(new TestIdB("1")),
valueNode(new TestIdB("3")));
assertThat(root2).isEqualTo(expected2);
}
/**
* Test a node with no children is added when there is no market data function for an ID.
*/
public void noMarketDataBuilder() {
MarketDataNode expected =
rootNode(
valueNode(new TestIdC("1")),
valueNode(
new TestIdB("2"),
valueNode(new TestIdC("3"))));
MarketDataRequirements requirements =
MarketDataRequirements.builder()
.addValues(new TestIdC("1"), new TestIdB("2"))
.build();
MarketDataRequirements id2Reqs =
MarketDataRequirements.builder()
.addValues(new TestIdC("3"))
.build();
TestMarketDataFunctionB builder = new TestMarketDataFunctionB(ImmutableMap.of(new TestIdB("2"), id2Reqs));
ImmutableMap<Class<? extends MarketDataId<?>>, MarketDataFunction<?, ?>> functions =
ImmutableMap.of(TestIdB.class, builder);
// Build the tree without providing a market data function to handle TestId3
MarketDataNode root =
MarketDataNode.buildDependencyTree(
requirements,
BuiltScenarioMarketData.empty(),
MarketDataConfig.empty(),
functions);
assertThat(root).isEqualTo(expected);
}
//-------------------------------------------------------------------------
private static MarketDataNode rootNode(MarketDataNode... children) {
return MarketDataNode.root(Arrays.asList(children));
}
private static MarketDataNode valueNode(MarketDataId<?> id, MarketDataNode... children) {
return MarketDataNode.child(id, MarketDataNode.DataType.SINGLE_VALUE, Arrays.asList(children));
}
private static MarketDataNode observableNode(ObservableId id) {
return MarketDataNode.leaf(id, MarketDataNode.DataType.SINGLE_VALUE);
}
private static MarketDataNode timeSeriesNode(ObservableId id) {
return MarketDataNode.leaf(id, MarketDataNode.DataType.TIME_SERIES);
}
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 idA = (TestIdA) o;
return Objects.equals(id, idA.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public String toString() {
return "TestId1 [id=" + id + "]";
}
}
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 idB = (TestIdB) o;
return Objects.equals(str, idB.str);
}
@Override
public int hashCode() {
return Objects.hash(str);
}
@Override
public String toString() {
return "TestId2 [str='" + str + "']";
}
}
private static final class TestIdC implements MarketDataId<String> {
private final String id;
private TestIdC(String id) {
this.id = id;
}
@Override
public Class<String> getMarketDataType() {
return String.class;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TestIdC idC = (TestIdC) o;
return Objects.equals(id, idC.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public String toString() {
return "BazId [id='" + id + "']";
}
}
private static final class TestMarketDataFunctionA implements MarketDataFunction<Double, TestIdA> {
@Override
public MarketDataRequirements requirements(TestIdA id, MarketDataConfig marketDataConfig) {
// The ID represents observable data which has no dependencies by definition
return MarketDataRequirements.empty();
}
@Override
public MarketDataBox<Double> build(
TestIdA id,
MarketDataConfig marketDataConfig,
ScenarioMarketData marketData,
ReferenceData refData) {
throw new UnsupportedOperationException("build not implemented");
}
@Override
public Class<TestIdA> getMarketDataIdType() {
return TestIdA.class;
}
}
private static final class TestMarketDataB {
}
private static final class TestMarketDataFunctionB implements MarketDataFunction<TestMarketDataB, TestIdB> {
private final Map<TestIdB, MarketDataRequirements> requirements;
private TestMarketDataFunctionB(Map<TestIdB, MarketDataRequirements> requirements) {
this.requirements = requirements;
}
@Override
public MarketDataRequirements requirements(TestIdB id, MarketDataConfig marketDataConfig) {
return requirements.getOrDefault(id, MarketDataRequirements.empty());
}
@Override
public MarketDataBox<TestMarketDataB> build(
TestIdB id,
MarketDataConfig marketDataConfig,
ScenarioMarketData marketData,
ReferenceData refData) {
throw new UnsupportedOperationException("build not implemented");
}
@Override
public Class<TestIdB> getMarketDataIdType() {
return TestIdB.class;
}
}
}