/**
* Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.market.curve;
import static com.opengamma.strata.collect.Guavate.toImmutableList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.collect.MapStream;
import com.opengamma.strata.collect.array.DoubleMatrix;
import com.opengamma.strata.collect.tuple.Pair;
import com.opengamma.strata.market.ShiftType;
/**
* Mutable builder for building instances of {@link CurvePointShifts}.
* <p>
* This is created via {@link CurvePointShifts#builder(ShiftType)}.
*/
public final class CurvePointShiftsBuilder {
/**
* The type of shift to apply to the rates.
*/
private final ShiftType shiftType;
/**
* The shift amounts, keyed by the identifier of the node to which they should be applied.
* <p>
* This is a linked map in order to preserve the insertion order. This means the node identifiers
* will appear in the same order as the nodes, assuming the shifts are added in node order (which seems
* likely).
*/
private final Map<Pair<Integer, Object>, Double> shifts = new LinkedHashMap<>();
//-------------------------------------------------------------------------
/**
* Restricted constructor used by {@link CurvePointShifts#builder}.
*
* @param shiftType the type of shift to apply to the rates
*/
CurvePointShiftsBuilder(ShiftType shiftType) {
this.shiftType = ArgChecker.notNull(shiftType, "shiftType");
}
//-------------------------------------------------------------------------
/**
* Adds a shift for a curve node to the builder.
*
* @param scenarioIndex the index of the scenario containing the shift
* @param nodeIdentifier the identifier of the node to which the shift should be applied
* @param shiftAmount the size of the shift
* @return this builder
*/
public CurvePointShiftsBuilder addShift(int scenarioIndex, Object nodeIdentifier, double shiftAmount) {
ArgChecker.notNull(nodeIdentifier, "nodeIdentifier");
ArgChecker.notNegative(scenarioIndex, "scenarioIndex");
shifts.put(Pair.of(scenarioIndex, nodeIdentifier), shiftAmount);
return this;
}
/**
* Adds multiple shifts to the builder.
*
* @param scenarioIndex the index of the scenario containing the shifts
* @param shiftMap the shift amounts, keyed by the identifier of the node to which they should be applied
* @return this builder
*/
public CurvePointShiftsBuilder addShifts(int scenarioIndex, Map<?, Double> shiftMap) {
ArgChecker.notNull(shiftMap, "shiftMap");
ArgChecker.notNegative(scenarioIndex, "scenarioIndex");
MapStream.of(shiftMap).forEach((id, shift) -> shifts.put(Pair.of(scenarioIndex, id), shift));
return this;
}
//-------------------------------------------------------------------------
/**
* Returns an instance of {@link CurvePointShifts} built from the data in this builder.
*
* @return an instance of {@link CurvePointShifts} built from the data in this builder
*/
public CurvePointShifts build() {
// This finds the scenario count by finding the maximum index and adding 1.
// If OptionalInt had map() it could be written more sensibly as: ...max().map(i -> i + 1).orElse(0)
// but it doesn't, hence using -1 and adding 1 to it for the case of zero scenarios
int scenarioCount = shifts.keySet().stream()
.mapToInt(Pair::getFirst)
.max()
.orElse(-1) + 1;
List<Object> nodeIdentifiers = shifts.keySet().stream()
.map(Pair::getSecond)
.distinct() // Use distinct to preserve order. Collecting to a set wouldn't preserve it
.collect(toImmutableList());
DoubleMatrix shiftMatrix =
DoubleMatrix.of(scenarioCount, nodeIdentifiers.size(), (r, c) -> shiftValue(r, nodeIdentifiers.get(c)));
return new CurvePointShifts(shiftType, shiftMatrix, nodeIdentifiers);
}
private double shiftValue(int scenarioIndex, Object nodeIdentifier) {
Double shift = shifts.get(Pair.of(scenarioIndex, nodeIdentifier));
return shift != null ? shift : 0;
}
}