/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.analytics.model.curve; import static com.opengamma.engine.value.ValueRequirementNames.G2PP_PARAMETERS; import static com.opengamma.financial.analytics.model.curve.CurveCalculationPropertyNamesAndValues.PROPERTY_G2PP_PARAMETERS; import it.unimi.dsi.fastutil.doubles.DoubleArrayList; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.threeten.bp.Clock; import org.threeten.bp.Instant; import org.threeten.bp.LocalTime; import org.threeten.bp.ZoneOffset; import org.threeten.bp.ZonedDateTime; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.analytics.financial.model.interestrate.definition.G2ppPiecewiseConstantParameters; import com.opengamma.analytics.util.time.TimeCalculator; import com.opengamma.core.value.MarketDataRequirementNames; import com.opengamma.engine.ComputationTarget; import com.opengamma.engine.ComputationTargetSpecification; import com.opengamma.engine.function.AbstractFunction; import com.opengamma.engine.function.CompiledFunctionDefinition; import com.opengamma.engine.function.FunctionCompilationContext; import com.opengamma.engine.function.FunctionExecutionContext; import com.opengamma.engine.function.FunctionInputs; import com.opengamma.engine.target.ComputationTargetType; import com.opengamma.engine.value.ComputedValue; import com.opengamma.engine.value.ValueProperties; import com.opengamma.engine.value.ValueRequirement; import com.opengamma.engine.value.ValueSpecification; import com.opengamma.financial.analytics.parameters.G2ppParameters; import com.opengamma.financial.config.ConfigSourceQuery; import com.opengamma.id.ExternalId; import com.opengamma.id.ExternalScheme; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.async.AsynchronousExecution; import com.opengamma.util.money.Currency; import com.opengamma.util.time.Tenor; import com.opengamma.util.tuple.Pair; /** * Function that supplies G2++ parameters. */ public class G2ppParametersFunction extends AbstractFunction { /** The logger */ private static final Logger s_logger = LoggerFactory.getLogger(G2ppParametersFunction.class); /** The default first volatility term structure */ private static final Map<Tenor, Double> FIRST_VOLATILITY_TERMS = new LinkedHashMap<>(); /** The default second volatility term structure */ private static final Map<Tenor, Double> SECOND_VOLATILITY_TERMS = new LinkedHashMap<>(); static { FIRST_VOLATILITY_TERMS.put(Tenor.THREE_MONTHS, 0.01d); FIRST_VOLATILITY_TERMS.put(Tenor.TWELVE_MONTHS, 0.01d); FIRST_VOLATILITY_TERMS.put(Tenor.TWO_YEARS, 0.01d); FIRST_VOLATILITY_TERMS.put(Tenor.THREE_YEARS, 0.01d); FIRST_VOLATILITY_TERMS.put(Tenor.FOUR_YEARS, 0.01d); FIRST_VOLATILITY_TERMS.put(Tenor.FIVE_YEARS, 0.01d); SECOND_VOLATILITY_TERMS.put(Tenor.THREE_MONTHS, 0.01d); SECOND_VOLATILITY_TERMS.put(Tenor.TWELVE_MONTHS, 0.01d); SECOND_VOLATILITY_TERMS.put(Tenor.TWO_YEARS, 0.01d); SECOND_VOLATILITY_TERMS.put(Tenor.THREE_YEARS, 0.01d); SECOND_VOLATILITY_TERMS.put(Tenor.FOUR_YEARS, 0.01d); SECOND_VOLATILITY_TERMS.put(Tenor.FIVE_YEARS, 0.01d); } /** The default first mean reversion default */ private static final Double FIRST_MEAN_REVERSION_DEFAULT = 0.01; /** The default second mean reversion default */ private static final Double SECOND_MEAN_REVERSION_DEFAULT = 0.01; /** The default first initial volatility */ private static final Double FIRST_INITIAL_VOLATILITY_DEFAULT = 0.01; /** The default second initial volatility */ private static final Double SECOND_INITIAL_VOLATILITY_DEFAULT = 0.01; /** The default correlation */ private static final Double CORRELATION_DEFAULT = 0.5; /** The configuration name */ private final String _name; /** The currency for which these parameters are valid */ private final Currency _currency; private ConfigSourceQuery<G2ppParameters> _g2ppParameters; /** * @param name The name of the G2++ parameter set, not null * @param currency The currency for which the parameters are valid, not null */ public G2ppParametersFunction(final String name, final String currency) { ArgumentChecker.notNull(name, "name"); ArgumentChecker.notNull(currency, "currency"); _name = name; _currency = Currency.of(currency); } @Override public void init(final FunctionCompilationContext context) { _g2ppParameters = ConfigSourceQuery.init(context, this, G2ppParameters.class); } @Override public CompiledFunctionDefinition compile(final FunctionCompilationContext context, final Instant atInstant) { final ValueProperties properties = createValueProperties().with(PROPERTY_G2PP_PARAMETERS, _name).get(); final ValueSpecification result = new ValueSpecification(G2PP_PARAMETERS, ComputationTargetSpecification.of(_currency), properties); final Set<ValueRequirement> requirements = new HashSet<>(); final G2ppParameters parameters = _g2ppParameters.get(_name); if (parameters == null) { throw new OpenGammaRuntimeException("G2ppParameter configuration called " + _name + " was null"); } requirements.add(new ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, ComputationTargetType.PRIMITIVE, parameters.getFirstMeanReversionId())); requirements.add(new ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, ComputationTargetType.PRIMITIVE, parameters.getSecondMeanReversionId())); requirements.add(new ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, ComputationTargetType.PRIMITIVE, parameters.getFirstInitialVolatilityId())); requirements.add(new ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, ComputationTargetType.PRIMITIVE, parameters.getSecondInitialVolatilityId())); requirements.add(new ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, ComputationTargetType.PRIMITIVE, parameters.getCorrelationId())); final Map<Tenor, Pair<ExternalId, ExternalId>> volatilityTermStructure = parameters.getVolatilityTermStructure(); for (final Map.Entry<Tenor, Pair<ExternalId, ExternalId>> entry : volatilityTermStructure.entrySet()) { final ExternalScheme firstScheme = entry.getValue().getFirst().getScheme(); final ExternalScheme secondScheme = entry.getValue().getSecond().getScheme(); final String firstId = entry.getValue().getFirst().getValue(); final String secondId = entry.getValue().getSecond().getValue(); final ExternalId firstTenorAppendedId = ExternalId.of(firstScheme, createId(entry.getKey(), firstId)); final ExternalId secondTenorAppendedId = ExternalId.of(secondScheme, createId(entry.getKey(), secondId)); requirements.add(new ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, ComputationTargetType.PRIMITIVE, firstTenorAppendedId)); requirements.add(new ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, ComputationTargetType.PRIMITIVE, secondTenorAppendedId)); } final ZonedDateTime atZDT = ZonedDateTime.ofInstant(atInstant, ZoneOffset.UTC); return new AbstractInvokingCompiledFunction(atZDT.with(LocalTime.MIDNIGHT), atZDT.plusDays(1).with(LocalTime.MIDNIGHT).minusNanos(1000000)) { @SuppressWarnings("synthetic-access") @Override public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) throws AsynchronousExecution { final Clock snapshotClock = executionContext.getValuationClock(); final ZonedDateTime now = ZonedDateTime.now(snapshotClock); Object firstMeanReversionObject = inputs .getValue(new ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, ComputationTargetType.PRIMITIVE, parameters.getFirstMeanReversionId())); if (firstMeanReversionObject == null) { //TODO remove this when we can handle the configurations properly firstMeanReversionObject = FIRST_MEAN_REVERSION_DEFAULT; } Object secondMeanReversionObject = inputs.getValue(new ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, ComputationTargetType.PRIMITIVE, parameters .getSecondMeanReversionId())); if (secondMeanReversionObject == null) { //TODO remove this when we can handle the configurations properly secondMeanReversionObject = SECOND_MEAN_REVERSION_DEFAULT; } Object firstInitialVolatilityObject = inputs.getValue(new ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, ComputationTargetType.PRIMITIVE, parameters .getFirstInitialVolatilityId())); if (firstInitialVolatilityObject == null) { //TODO remove this when we can handle the configurations properly firstInitialVolatilityObject = FIRST_INITIAL_VOLATILITY_DEFAULT; } Object secondInitialVolatilityObject = inputs.getValue(new ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, ComputationTargetType.PRIMITIVE, parameters .getSecondInitialVolatilityId())); if (secondInitialVolatilityObject == null) { //TODO remove this when we can handle the configurations properly secondInitialVolatilityObject = SECOND_INITIAL_VOLATILITY_DEFAULT; } Object correlationObject = inputs.getValue(new ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, ComputationTargetType.PRIMITIVE, parameters.getCorrelationId())); if (correlationObject == null) { //TODO remove this when we can handle the configurations properly correlationObject = CORRELATION_DEFAULT; } final Double firstMeanReversion = (Double) firstMeanReversionObject; final Double secondMeanReversion = (Double) secondMeanReversionObject; final Double firstInitialVolatility = (Double) firstInitialVolatilityObject; final Double secondInitialVolatility = (Double) secondInitialVolatilityObject; final Double correlation = (Double) correlationObject; final DoubleArrayList firstVolatility = new DoubleArrayList(); firstVolatility.add(firstInitialVolatility); final DoubleArrayList secondVolatility = new DoubleArrayList(); secondVolatility.add(secondInitialVolatility); final DoubleArrayList volatilityTime = new DoubleArrayList(); for (final Map.Entry<Tenor, Pair<ExternalId, ExternalId>> entry : volatilityTermStructure.entrySet()) { final ExternalScheme firstScheme = entry.getValue().getFirst().getScheme(); final ExternalScheme secondScheme = entry.getValue().getSecond().getScheme(); final String firstId = entry.getValue().getFirst().getValue(); final String secondId = entry.getValue().getSecond().getValue(); final ExternalId firstTenorAppendedId = ExternalId.of(firstScheme, createId(entry.getKey(), firstId)); final ExternalId secondTenorAppendedId = ExternalId.of(secondScheme, createId(entry.getKey(), secondId)); Object firstVolatilityObject = inputs.getValue(new ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, ComputationTargetType.PRIMITIVE, firstTenorAppendedId)); //TODO remove block this when we can handle the configurations properly if (firstVolatilityObject == null) { firstVolatilityObject = FIRST_VOLATILITY_TERMS.get(entry.getKey()); } Object secondVolatilityObject = inputs.getValue(new ValueRequirement(MarketDataRequirementNames.MARKET_VALUE, ComputationTargetType.PRIMITIVE, secondTenorAppendedId)); //TODO remove block this when we can handle the configurations properly if (secondVolatilityObject == null) { secondVolatilityObject = SECOND_VOLATILITY_TERMS.get(entry.getKey()); } if (firstVolatilityObject == null) { s_logger.error("Could not get value for " + firstTenorAppendedId); continue; } if (secondVolatilityObject == null) { s_logger.error("Could not get value for " + secondTenorAppendedId); } else { final double t = TimeCalculator.getTimeBetween(now, now.plus(entry.getKey().getPeriod())); firstVolatility.add((Double) firstVolatilityObject); secondVolatility.add((Double) secondVolatilityObject); volatilityTime.add(t); } } final G2ppPiecewiseConstantParameters g2ppParameters = new G2ppPiecewiseConstantParameters(new double[] {firstMeanReversion, secondMeanReversion }, new double[][] { firstVolatility.toDoubleArray(), secondVolatility.toDoubleArray() }, volatilityTime.toDoubleArray(), correlation); return Collections.singleton(new ComputedValue(result, g2ppParameters)); } @Override public ComputationTargetType getTargetType() { return ComputationTargetType.CURRENCY; } @SuppressWarnings("synthetic-access") @Override public boolean canApplyTo(final FunctionCompilationContext compilationContext, final ComputationTarget target) { return _currency.equals(target.getValue()); } @Override public Set<ValueSpecification> getResults(final FunctionCompilationContext compilationContext, final ComputationTarget target) { return Collections.singleton(result); } @Override public Set<ValueRequirement> getRequirements(final FunctionCompilationContext compilationContext, final ComputationTarget target, final ValueRequirement desiredValue) { final ValueProperties constraints = desiredValue.getConstraints(); final Set<String> names = constraints.getValues(PROPERTY_G2PP_PARAMETERS); if (names == null || names.size() != 1) { return null; } return requirements; } @Override public boolean canHandleMissingRequirements() { return true; } @Override public boolean canHandleMissingInputs() { return true; } }; } /** * Appends the tenor to an id to create the market data identifier. * * @param tenor The tenor * @param id The id * @return The market data id */ static String createId(final Tenor tenor, final String id) { final StringBuilder newId = new StringBuilder(id); newId.append("_"); newId.append(tenor.getPeriod().toString()); return newId.toString(); } }