/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.sesame;
import static com.opengamma.financial.convention.initializer.PerCurrencyConventionHelper.DEPOSIT;
import static com.opengamma.financial.convention.initializer.PerCurrencyConventionHelper.getConventionLink;
import static com.opengamma.util.result.FailureStatus.ERROR;
import static com.opengamma.util.result.FailureStatus.MISSING_DATA;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threeten.bp.LocalDate;
import org.threeten.bp.ZonedDateTime;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.analytics.financial.curve.interestrate.generator.GeneratorCurveYieldInterpolated;
import com.opengamma.analytics.financial.curve.interestrate.generator.GeneratorCurveYieldInterpolatedAnchorNode;
import com.opengamma.analytics.financial.curve.interestrate.generator.GeneratorYDCurve;
import com.opengamma.analytics.financial.forex.method.FXMatrix;
import com.opengamma.analytics.financial.instrument.InstrumentDefinition;
import com.opengamma.analytics.financial.instrument.index.IborIndex;
import com.opengamma.analytics.financial.instrument.index.IndexON;
import com.opengamma.analytics.financial.interestrate.InstrumentDerivative;
import com.opengamma.analytics.financial.interestrate.cash.derivative.Cash;
import com.opengamma.analytics.financial.provider.calculator.discounting.ParRateDiscountingCalculator;
import com.opengamma.analytics.financial.provider.calculator.discounting.ParSpreadMarketQuoteCurveSensitivityDiscountingCalculator;
import com.opengamma.analytics.financial.provider.calculator.discounting.ParSpreadMarketQuoteDiscountingCalculator;
import com.opengamma.analytics.financial.provider.calculator.generic.LastTimeCalculator;
import com.opengamma.analytics.financial.provider.curve.CurveBuildingBlockBundle;
import com.opengamma.analytics.financial.provider.curve.MultiCurveBundle;
import com.opengamma.analytics.financial.provider.curve.SingleCurveBundle;
import com.opengamma.analytics.financial.provider.curve.multicurve.MulticurveDiscountBuildingRepository;
import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount;
import com.opengamma.analytics.financial.provider.description.interestrate.ProviderUtils;
import com.opengamma.analytics.financial.schedule.ScheduleCalculator;
import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolatorFactory;
import com.opengamma.analytics.math.interpolation.Interpolator1D;
import com.opengamma.analytics.util.time.TimeCalculator;
import com.opengamma.core.convention.Convention;
import com.opengamma.core.holiday.HolidaySource;
import com.opengamma.core.link.ConventionLink;
import com.opengamma.core.link.SecurityLink;
import com.opengamma.core.marketdatasnapshot.SnapshotDataBundle;
import com.opengamma.financial.analytics.conversion.CalendarUtils;
import com.opengamma.financial.analytics.curve.AbstractCurveDefinition;
import com.opengamma.financial.analytics.curve.AbstractCurveSpecification;
import com.opengamma.financial.analytics.curve.ConverterUtils;
import com.opengamma.financial.analytics.curve.CurveConstructionConfiguration;
import com.opengamma.financial.analytics.curve.CurveDefinition;
import com.opengamma.financial.analytics.curve.CurveGroupConfiguration;
import com.opengamma.financial.analytics.curve.CurveSpecification;
import com.opengamma.financial.analytics.curve.CurveTypeConfiguration;
import com.opengamma.financial.analytics.curve.DiscountingCurveTypeConfiguration;
import com.opengamma.financial.analytics.curve.FixedDateInterpolatedCurveDefinition;
import com.opengamma.financial.analytics.curve.IborCurveTypeConfiguration;
import com.opengamma.financial.analytics.curve.InterpolatedCurveDefinition;
import com.opengamma.financial.analytics.curve.OvernightCurveTypeConfiguration;
import com.opengamma.financial.analytics.ircurve.strips.CurveNode;
import com.opengamma.financial.analytics.ircurve.strips.CurveNodeWithIdentifier;
import com.opengamma.financial.convention.DepositConvention;
import com.opengamma.financial.convention.IborIndexConvention;
import com.opengamma.financial.convention.OvernightIndexConvention;
import com.opengamma.financial.convention.businessday.BusinessDayConvention;
import com.opengamma.financial.convention.calendar.Calendar;
import com.opengamma.financial.convention.daycount.DayCount;
import com.opengamma.financial.security.index.OvernightIndex;
import com.opengamma.id.ExternalIdBundle;
import com.opengamma.sesame.component.StringSet;
import com.opengamma.sesame.marketdata.MulticurveMarketDataBuilder;
import com.opengamma.util.money.Currency;
import com.opengamma.util.result.FailureStatus;
import com.opengamma.util.result.Result;
import com.opengamma.util.time.Tenor;
import com.opengamma.util.tuple.Pair;
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
/**
* Function implementation that provides a discounting multi-curve bundle.
*
* @deprecated curves are built using {@link MulticurveMarketDataBuilder}.
*/
@Deprecated
public class DefaultDiscountingMulticurveBundleFn implements DiscountingMulticurveBundleFn {
private static final Logger s_logger = LoggerFactory.getLogger(DefaultDiscountingMulticurveBundleFn.class);
private static final ParSpreadMarketQuoteDiscountingCalculator DISCOUNTING_CALCULATOR =
ParSpreadMarketQuoteDiscountingCalculator.getInstance();
private static final ParSpreadMarketQuoteCurveSensitivityDiscountingCalculator CURVE_SENSITIVITY_CALCULATOR =
ParSpreadMarketQuoteCurveSensitivityDiscountingCalculator.getInstance();
private final CurveDefinitionFn _curveDefinitionProvider;
private final CurveSpecificationFn _curveSpecificationProvider;
private final CurveSpecificationMarketDataFn _curveSpecificationMarketDataProvider;
private final FXMatrixFn _fxMatrixProvider;
private final HolidaySource _holidaySource;
private final CurveNodeConverterFn _curveNodeConverter;
private final RootFinderConfiguration _rootFinderConfiguration;
/**
* Map indicating which curves should be implied, and if so which curves they should
* be implied from.
*/
// todo - this is only a temporary solution to determine the implied deposit curves
private final Set<String> _impliedCurveNames;
private final CurveNodeInstrumentDefinitionFactory _curveNodeInstrumentDefinitionFactory;
public DefaultDiscountingMulticurveBundleFn(CurveDefinitionFn curveDefinitionProvider,
CurveSpecificationFn curveSpecificationProvider,
CurveSpecificationMarketDataFn curveSpecificationMarketDataProvider,
FXMatrixFn fxMatrixProvider,
HolidaySource holidaySource,
CurveNodeConverterFn curveNodeConverter,
RootFinderConfiguration rootFinderConfiguration,
CurveNodeInstrumentDefinitionFactory curveNodeInstrumentDefinitionFactory,
StringSet impliedCurveNames) {
_curveDefinitionProvider = curveDefinitionProvider;
_curveSpecificationProvider = curveSpecificationProvider;
_curveSpecificationMarketDataProvider = curveSpecificationMarketDataProvider;
_fxMatrixProvider = fxMatrixProvider;
_holidaySource = holidaySource;
_curveNodeConverter = curveNodeConverter;
_rootFinderConfiguration = rootFinderConfiguration;
_impliedCurveNames = impliedCurveNames.getStrings();
_curveNodeInstrumentDefinitionFactory = curveNodeInstrumentDefinitionFactory;
}
//-------------------------------------------------------------------------
@Override
public Result<MulticurveBundle> generateBundle(
Environment env, CurveConstructionConfiguration curveConfig,
Map<CurveConstructionConfiguration, Result<MulticurveBundle>> requiredCurves) {
if (s_logger.isDebugEnabled()) {
List<String> requiredCurveNames = new ArrayList<>(requiredCurves.size());
for (CurveConstructionConfiguration requiredCurveConfig : requiredCurves.keySet()) {
requiredCurveNames.add(requiredCurveConfig.getName());
}
s_logger.debug("Generating bundle '{}', requiredCurves {}, valuationTime {}",
curveConfig.getName(), requiredCurveNames, env.getValuationTime());
}
// Each curve config may have one or more exogenous requirements which basically should
// point to another curve config (which may point to one or more configs ...)
// We need a depth-first evaluation of the tree formed by these configs as (direct) child
// MulticurveProviderInterface instances are required to be passed into their parent's
// evaluation via the known data parameter
// If we can't build due to insufficient market data, then we keep going but don't call
// the final build step. This way we ensure that market data requirements have been captured.
// todo check for cycles in the config
Result<FXMatrix> fxMatrixResult = _fxMatrixProvider.getFXMatrix(env, curveConfig);
Result<MulticurveProviderDiscount> exogenousBundles =
buildExogenousBundles(env, curveConfig, requiredCurves, fxMatrixResult);
return getCurves(env, curveConfig, exogenousBundles, fxMatrixResult);
}
@Override
public Result<ImpliedDepositCurveData> extractImpliedDepositCurveData(
Environment env,
CurveConstructionConfiguration curveConfig,
Map<CurveConstructionConfiguration, Result<MulticurveBundle>> builtCurves) {
// todo - this implementation is nowhere near complete
Result<FXMatrix> fxMatrixResult = _fxMatrixProvider.getFXMatrix(env, curveConfig);
Result<MulticurveProviderDiscount> exogenousBundles = buildExogenousBundles(env, curveConfig, builtCurves, fxMatrixResult);
CurveGroupConfiguration group = curveConfig.getCurveGroups().get(0);
Map.Entry<String, List<? extends CurveTypeConfiguration>> type = group.getTypesForCurves().entrySet().iterator().next();
Result<CurveDefinition> curveDefinition = _curveDefinitionProvider.getCurveDefinition(type.getKey());
// Any one of the above 3 could have failed, but we have attempted all to
// try and report as many errors as possible as early as possible
if (Result.allSuccessful(fxMatrixResult, exogenousBundles, curveDefinition)) {
DiscountingCurveTypeConfiguration typeConfiguration = (DiscountingCurveTypeConfiguration) type.getValue().get(0);
Currency currency = Currency.of(typeConfiguration.getReference());
return Result.success(extractImpliedDepositCurveData(currency,
curveDefinition.getValue(),
exogenousBundles.getValue(),
env.getValuationTime())); // TODO can this be the valuation date?
} else {
return Result.failure(fxMatrixResult, exogenousBundles, curveDefinition);
}
}
// REVIEW Chris 2014-03-05 - the return type needs to be a class
private ImpliedDepositCurveData extractImpliedDepositCurveData(Currency currency,
CurveDefinition impliedCurveDefinition,
MulticurveProviderDiscount multicurves,
ZonedDateTime valuationTime) {
final ParRateDiscountingCalculator parRateDiscountingCalculator = ParRateDiscountingCalculator.getInstance();
final Calendar calendar = CalendarUtils.getCalendar(_holidaySource, currency);
final List<Tenor> tenors = new ArrayList<>();
final List<Double> parRates = new ArrayList<>();
final List<InstrumentDerivative> cashNodes = new ArrayList<>();
ConventionLink<Convention> conventionLink = getConventionLink(currency, DEPOSIT);
DepositConvention currencyDepositConvention = (DepositConvention) conventionLink.resolve();
int spotLag = currencyDepositConvention.getSettlementDays();
ZonedDateTime spotDate = ScheduleCalculator.getAdjustedDate(valuationTime, spotLag, calendar);
DayCount dayCount = currencyDepositConvention.getDayCount();
BusinessDayConvention businessDayConvention = currencyDepositConvention.getBusinessDayConvention();
for (final CurveNode node : impliedCurveDefinition.getNodes()) {
final Tenor tenor = node.getResolvedMaturity();
final ZonedDateTime paymentDate =
ScheduleCalculator.getAdjustedDate(spotDate, tenor.getPeriod(), businessDayConvention, calendar, currencyDepositConvention.isIsEOM());
final double startTime = TimeCalculator.getTimeBetween(valuationTime, spotDate);
final double endTime = TimeCalculator.getTimeBetween(valuationTime, paymentDate);
final double accrualFactor = dayCount.getDayCountFraction(spotDate, paymentDate, calendar);
final Cash cashDepositNode = new Cash(currency, startTime, endTime, 1, 0, accrualFactor);
final double parRate = parRateDiscountingCalculator.visitCash(cashDepositNode, multicurves);
tenors.add(tenor);
cashNodes.add(new Cash(currency, startTime, endTime, 1, parRate, accrualFactor));
parRates.add(parRate);
}
return new ImpliedDepositCurveData(tenors, parRates, cashNodes);
}
private MulticurveDiscountBuildingRepository createBuilder() {
return new MulticurveDiscountBuildingRepository(
_rootFinderConfiguration.getAbsoluteTolerance(),
_rootFinderConfiguration.getRelativeTolerance(),
_rootFinderConfiguration.getMaxIterations());
}
// TODO sort this out [SSM-164]
private Result<MulticurveBundle> getCurves(
Environment env, CurveConstructionConfiguration config, Result<MulticurveProviderDiscount> exogenousBundle,
Result<FXMatrix> fxMatrixResult) {
final int nGroups = config.getCurveGroups().size();
@SuppressWarnings("unchecked")
final MultiCurveBundle<GeneratorYDCurve>[] curveBundles = new MultiCurveBundle[nGroups];
final LinkedHashMap<String, Currency> discountingMap = new LinkedHashMap<>();
final LinkedHashMap<String, IborIndex[]> forwardIborMap = new LinkedHashMap<>();
final LinkedHashMap<String, IndexON[]> forwardONMap = new LinkedHashMap<>();
//TODO comparator to sort groups by order
int i = 0; // Implementation Note: loop on the groups
final Set<Currency> curvesToRemove = new HashSet<>();
// Result to allow us to capture any failures in all these loops, the
// actual value if a success is of no consequence
Result<Boolean> curveBundleResult = Result.success(true);
for (final CurveGroupConfiguration group : config.getCurveGroups()) { // Group - start
final int nCurves = group.getTypesForCurves().size();
@SuppressWarnings("unchecked")
final SingleCurveBundle<GeneratorYDCurve>[] singleCurves = new SingleCurveBundle[nCurves];
int j = 0;
for (final Map.Entry<AbstractCurveDefinition, List<? extends CurveTypeConfiguration>> entry : group.resolveTypesForCurves().entrySet()) {
AbstractCurveDefinition curve = entry.getKey();
String curveName = curve.getName();
if (_impliedCurveNames.contains(curveName)) {
if (!(curve instanceof CurveDefinition)) {
Result.failure(curveBundleResult, Result.failure(FailureStatus.ERROR, "Curve {} was configured in " +
"function as an implied depo curve but is not a subclass of CurveDefinition in the db.", curveName));
} else if (!exogenousBundle.isSuccess()) {
curveBundleResult = Result.failure(curveBundleResult, exogenousBundle);
} else {
Currency currency = null;
for (CurveTypeConfiguration type : entry.getValue()) {
if (type instanceof DiscountingCurveTypeConfiguration) {
final String reference = ((DiscountingCurveTypeConfiguration) type).getReference();
try {
currency = Currency.of(reference);
} catch (final IllegalArgumentException e) {
throw new OpenGammaRuntimeException("Cannot handle reference type " + reference + " for discounting curves");
}
}
}
// TODO can this be valuationDate?
singleCurves[j] = buildImpliedDepositCurve(currency, (CurveDefinition) curve, exogenousBundle.getValue(), env.getValuationTime());
// todo note we do this below as well, refactor it to be common
discountingMap.put(curveName, currency);
// This curve needs to replace the existing discounting curve of the same currency
curvesToRemove.add(currency);
}
} else {
Result<AbstractCurveSpecification> curveSpecResult =
_curveSpecificationProvider.getCurveSpecification(env, curve);
if (curveSpecResult.isSuccess()) {
final CurveSpecification specification = (CurveSpecification) curveSpecResult.getValue();
Result<Map<ExternalIdBundle, Double>> marketDataResult =
_curveSpecificationMarketDataProvider.requestData(env, specification);
// Only proceed if we have all market data values available to us
if (Result.allSuccessful(fxMatrixResult, marketDataResult)) {
FXMatrix fxMatrix = fxMatrixResult.getValue();
// todo this is temporary to allow us to get up and running fast
final SnapshotDataBundle snapshot = createSnapshotDataBundle(marketDataResult.getValue());
final int nNodes = specification.getNodes().size();
final double[] parameterGuessForCurves = new double[nNodes];
Arrays.fill(parameterGuessForCurves, 0.02); // For FX forward, the FX rate is not a good initial guess. // TODO: change this // marketData
final Result<InstrumentDerivative[]> derivativesForCurve =
extractInstrumentDerivatives(env, specification, snapshot, fxMatrix, env.getValuationTime());
final List<IborIndex> iborIndex = new ArrayList<>();
final List<IndexON> overnightIndex = new ArrayList<>();
for (final CurveTypeConfiguration type : entry.getValue()) {
if (type instanceof DiscountingCurveTypeConfiguration) {
final String reference = ((DiscountingCurveTypeConfiguration) type).getReference();
try {
final Currency currency = Currency.of(reference);
//should this map check that the curve name has not already been entered?
discountingMap.put(curveName, currency);
} catch (final IllegalArgumentException e) {
throw new OpenGammaRuntimeException("Cannot handle reference type " + reference + " for discounting curves");
}
} else if (type instanceof IborCurveTypeConfiguration) {
iborIndex.add(createIborIndex((IborCurveTypeConfiguration) type));
} else if (type instanceof OvernightCurveTypeConfiguration) {
overnightIndex.add(createOvernightIndex((OvernightCurveTypeConfiguration) type));
} else {
Result<?> typeFailure =
Result.failure(ERROR, "Cannot handle curveTypeConfiguration with type {} whilst building curve: {}",
type.getClass(), curveName);
curveBundleResult = Result.failure(curveBundleResult, typeFailure);
}
}
if (!iborIndex.isEmpty()) {
forwardIborMap.put(curveName, iborIndex.toArray(new IborIndex[iborIndex.size()]));
}
if (!overnightIndex.isEmpty()) {
forwardONMap.put(curveName, overnightIndex.toArray(new IndexON[overnightIndex.size()]));
}
if (derivativesForCurve.isSuccess()) {
final GeneratorYDCurve generator = getGenerator(curve, env.getValuationDate());
singleCurves[j] = new SingleCurveBundle<>(curveName, derivativesForCurve.getValue(), generator.initialGuess(parameterGuessForCurves), generator);
} else {
curveBundleResult = Result.failure(curveBundleResult, derivativesForCurve);
}
} else {
curveBundleResult = Result.failure(curveBundleResult, fxMatrixResult, marketDataResult);
}
} else {
curveBundleResult = Result.failure(curveBundleResult, curveSpecResult);
}
}
j++;
}
if (curveBundleResult.isSuccess()) {
curveBundles[i++] = new MultiCurveBundle<>(singleCurves);
}
} // Group - end
if (Result.allSuccessful(exogenousBundle, curveBundleResult)) {
MulticurveProviderDiscount exogenousCurves = adjustMulticurveBundle(curvesToRemove, exogenousBundle.getValue());
Pair<MulticurveProviderDiscount, CurveBuildingBlockBundle> calibratedCurves =
createBuilder().makeCurvesFromDerivatives(
curveBundles, exogenousCurves, discountingMap, forwardIborMap,
forwardONMap, DISCOUNTING_CALCULATOR, CURVE_SENSITIVITY_CALCULATOR);
return Result.success(new MulticurveBundle(calibratedCurves.getFirst(), calibratedCurves.getSecond()));
} else {
return Result.failure(exogenousBundle, curveBundleResult);
}
}
private static SnapshotDataBundle createSnapshotDataBundle(Map<ExternalIdBundle, Double> marketData) {
SnapshotDataBundle snapshotDataBundle = new SnapshotDataBundle();
for (Map.Entry<ExternalIdBundle, Double> entry : marketData.entrySet()) {
snapshotDataBundle.setDataPoint(entry.getKey(), entry.getValue());
}
return snapshotDataBundle;
}
private IndexON createOvernightIndex(OvernightCurveTypeConfiguration type) {
OvernightIndex index = SecurityLink.resolvable(type.getConvention().toBundle(), OvernightIndex.class).resolve();
OvernightIndexConvention indexConvention =
ConventionLink.resolvable(index.getConventionId(), OvernightIndexConvention.class).resolve();
return ConverterUtils.indexON(index.getName(), indexConvention);
}
private IborIndex createIborIndex(IborCurveTypeConfiguration type) {
com.opengamma.financial.security.index.IborIndex indexSecurity =
SecurityLink.resolvable(type.getConvention(), com.opengamma.financial.security.index.IborIndex.class).resolve();
IborIndexConvention indexConvention =
ConventionLink.resolvable(indexSecurity.getConventionId(), IborIndexConvention.class).resolve();
return ConverterUtils.indexIbor(indexSecurity.getName(), indexConvention, indexSecurity.getTenor());
}
private MulticurveProviderDiscount adjustMulticurveBundle(Set<Currency> curvesToRemove,
MulticurveProviderDiscount multicurves) {
if (curvesToRemove.isEmpty()) {
return multicurves;
} else {
MulticurveProviderDiscount copy = multicurves.copy();
for (Currency currency : curvesToRemove) {
copy.removeCurve(currency);
}
return copy;
}
}
private SingleCurveBundle<GeneratorYDCurve> buildImpliedDepositCurve(Currency currency,
CurveDefinition impliedCurveDefinition,
MulticurveProviderDiscount multicurves,
ZonedDateTime valuationTime) {
ImpliedDepositCurveData impliedCurveData =
extractImpliedDepositCurveData(currency, impliedCurveDefinition, multicurves, valuationTime);
GeneratorYDCurve generator = getGenerator(impliedCurveDefinition, valuationTime.toLocalDate());
List<InstrumentDerivative> instrumentDerivatives = impliedCurveData.getCashNodes();
return new SingleCurveBundle<>(impliedCurveDefinition.getName(),
instrumentDerivatives.toArray(new InstrumentDerivative[instrumentDerivatives.size()]),
convertToArray(impliedCurveData.getParRates()), generator);
}
private double[] convertToArray(List<Double> first) {
double[] result = new double[first.size()];
for (int i = 0; i < first.size(); i++) {
result[i] = first.get(i);
}
return result;
}
private Result<InstrumentDerivative[]> extractInstrumentDerivatives(Environment env,
CurveSpecification specification,
SnapshotDataBundle snapshot,
FXMatrix fxMatrix,
ZonedDateTime valuationTime) {
Set<CurveNodeWithIdentifier> nodes = specification.getNodes();
List<InstrumentDerivative> derivativesForCurve = new ArrayList<>(nodes.size());
List<Result<?>> failures = new ArrayList<>();
for (CurveNodeWithIdentifier node : nodes) {
InstrumentDefinition<?> instrumentDefn =
_curveNodeInstrumentDefinitionFactory.createInstrumentDefinition(node, snapshot, valuationTime, fxMatrix);
Result<InstrumentDerivative> derivativeResult =
_curveNodeConverter.getDerivative(env, node, instrumentDefn, valuationTime);
if (derivativeResult.isSuccess()) {
derivativesForCurve.add(derivativeResult.getValue());
} else {
failures.add(derivativeResult);
}
}
if (failures.isEmpty()) {
return Result.success(derivativesForCurve.toArray(new InstrumentDerivative[derivativesForCurve.size()]));
} else {
return Result.failure(failures);
}
}
private GeneratorYDCurve getGenerator(final AbstractCurveDefinition definition, LocalDate valuationDate) {
if (definition instanceof InterpolatedCurveDefinition) {
final InterpolatedCurveDefinition interpolatedDefinition = (InterpolatedCurveDefinition) definition;
final String interpolatorName = interpolatedDefinition.getInterpolatorName();
final String leftExtrapolatorName = interpolatedDefinition.getLeftExtrapolatorName();
final String rightExtrapolatorName = interpolatedDefinition.getRightExtrapolatorName();
final Interpolator1D interpolator = CombinedInterpolatorExtrapolatorFactory.getInterpolator(interpolatorName,
leftExtrapolatorName,
rightExtrapolatorName);
if (definition instanceof FixedDateInterpolatedCurveDefinition) {
final FixedDateInterpolatedCurveDefinition fixedDateDefinition = (FixedDateInterpolatedCurveDefinition) definition;
final List<LocalDate> fixedDates = fixedDateDefinition.getFixedDates();
final DoubleArrayList nodePoints = new DoubleArrayList(fixedDates.size()); //TODO what about equal node points?
for (final LocalDate fixedDate : fixedDates) {
nodePoints.add(TimeCalculator.getTimeBetween(valuationDate, fixedDate)); //TODO what to do if the fixed date is before the valuation date?
}
final double anchor = nodePoints.get(0); //TODO should the anchor go into the definition?
return new GeneratorCurveYieldInterpolatedAnchorNode(nodePoints.toDoubleArray(), anchor, interpolator);
}
return new GeneratorCurveYieldInterpolated(LastTimeCalculator.getInstance(), interpolator);
}
throw new OpenGammaRuntimeException("Cannot handle curves of type " + definition.getClass());
}
private Result<MulticurveProviderDiscount> buildExogenousBundles(
Environment env, CurveConstructionConfiguration curveConfig,
Map<CurveConstructionConfiguration, Result<MulticurveBundle>> builtCurves,
Result<FXMatrix> fxMatrixResult) {
Result<Boolean> exogenousResult = Result.success(true);
Set<MulticurveProviderDiscount> exogenousBundles = new HashSet<>();
for (CurveConstructionConfiguration exogenousConfig : curveConfig.resolveCurveConfigurations()) {
Result<MulticurveBundle> bundleResult = builtCurves.get(exogenousConfig);
// This really shouldn't happen if using provided functions to call this one
// but that may not alwayus be the case
if (bundleResult == null) {
exogenousResult = Result.failure(
exogenousResult, Result.failure(MISSING_DATA, "No curve built for exogenous config: {}", exogenousConfig));
} else if (bundleResult.isSuccess()) {
exogenousBundles.add(bundleResult.getValue().getMulticurveProvider());
} else {
exogenousResult = Result.failure(exogenousResult, bundleResult);
}
}
if (Result.allSuccessful(exogenousResult, fxMatrixResult)) {
FXMatrix fxMatrix = fxMatrixResult.getValue();
if (exogenousBundles.isEmpty()) {
return Result.success(new MulticurveProviderDiscount(fxMatrix));
} else {
MulticurveProviderDiscount result = ProviderUtils.mergeDiscountingProviders(exogenousBundles);
MulticurveProviderDiscount provider = ProviderUtils.mergeDiscountingProviders(result, fxMatrix);
return Result.success(provider);
}
} else {
return Result.failure(fxMatrixResult, exogenousResult);
}
}
}