/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.analytics.model.fx;
import static com.opengamma.engine.value.ValuePropertyNames.CURRENCY;
import static com.opengamma.engine.value.ValuePropertyNames.CURVE;
import static com.opengamma.engine.value.ValuePropertyNames.CURVE_CONSTRUCTION_CONFIG;
import static com.opengamma.engine.value.ValuePropertyNames.CURVE_EXPOSURES;
import static com.opengamma.engine.value.ValuePropertyNames.FORWARD_CURVE_NAME;
import static com.opengamma.engine.value.ValueRequirementNames.CURRENCY_PAIRS;
import static com.opengamma.engine.value.ValueRequirementNames.CURVE_BUNDLE;
import static com.opengamma.engine.value.ValueRequirementNames.CURVE_DEFINITION;
import static com.opengamma.engine.value.ValueRequirementNames.CURVE_MARKET_DATA;
import static com.opengamma.engine.value.ValueRequirementNames.CURVE_SPECIFICATION;
import static com.opengamma.engine.value.ValueRequirementNames.FX_MATRIX;
import static com.opengamma.engine.value.ValueRequirementNames.JACOBIAN_BUNDLE;
import static com.opengamma.financial.analytics.model.CalculationPropertyNamesAndValues.FORWARD_POINTS;
import static com.opengamma.financial.analytics.model.curve.CurveCalculationPropertyNamesAndValues.PROPERTY_CURVE_TYPE;
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threeten.bp.Clock;
import org.threeten.bp.ZonedDateTime;
import com.google.common.collect.Iterables;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.analytics.financial.forex.derivative.Forex;
import com.opengamma.analytics.financial.forex.method.FXMatrix;
import com.opengamma.analytics.financial.instrument.InstrumentDefinition;
import com.opengamma.analytics.financial.interestrate.InstrumentDerivative;
import com.opengamma.analytics.financial.provider.curve.CurveBuildingBlockBundle;
import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount;
import com.opengamma.analytics.financial.provider.description.interestrate.ProviderUtils;
import com.opengamma.analytics.math.curve.DoublesCurve;
import com.opengamma.analytics.math.curve.InterpolatedDoublesCurve;
import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolatorFactory;
import com.opengamma.analytics.math.interpolation.Interpolator1D;
import com.opengamma.analytics.math.interpolation.Interpolator1DFactory;
import com.opengamma.core.config.ConfigSource;
import com.opengamma.core.holiday.HolidaySource;
import com.opengamma.core.marketdatasnapshot.SnapshotDataBundle;
import com.opengamma.core.region.RegionSource;
import com.opengamma.core.security.Security;
import com.opengamma.core.security.SecuritySource;
import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.engine.function.AbstractFunction;
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.ValuePropertyNames;
import com.opengamma.engine.value.ValueRequirement;
import com.opengamma.engine.value.ValueRequirementNames;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.financial.OpenGammaCompilationContext;
import com.opengamma.financial.analytics.conversion.FXForwardSecurityConverter;
import com.opengamma.financial.analytics.conversion.FixedIncomeConverterDataProvider;
import com.opengamma.financial.analytics.conversion.FutureTradeConverter;
import com.opengamma.financial.analytics.conversion.NonDeliverableFXForwardSecurityConverter;
import com.opengamma.financial.analytics.conversion.TradeConverter;
import com.opengamma.financial.analytics.curve.ConfigDBCurveConstructionConfigurationSource;
import com.opengamma.financial.analytics.curve.CurveConstructionConfiguration;
import com.opengamma.financial.analytics.curve.CurveDefinition;
import com.opengamma.financial.analytics.curve.CurveSpecification;
import com.opengamma.financial.analytics.curve.CurveUtils;
import com.opengamma.financial.analytics.curve.InterpolatedCurveDefinition;
import com.opengamma.financial.analytics.curve.exposure.ConfigDBInstrumentExposuresProvider;
import com.opengamma.financial.analytics.ircurve.strips.CurveNode;
import com.opengamma.financial.analytics.ircurve.strips.CurveNodeWithIdentifier;
import com.opengamma.financial.analytics.ircurve.strips.FXForwardNode;
import com.opengamma.financial.analytics.model.forex.ForexVisitors;
import com.opengamma.financial.analytics.timeseries.HistoricalTimeSeriesBundle;
import com.opengamma.financial.analytics.timeseries.HistoricalTimeSeriesFunctionUtils;
import com.opengamma.financial.convention.ConventionBundleSource;
import com.opengamma.financial.convention.ConventionSource;
import com.opengamma.financial.currency.CurrencyPair;
import com.opengamma.financial.currency.CurrencyPairs;
import com.opengamma.financial.security.FinancialSecurity;
import com.opengamma.financial.security.FinancialSecurityUtils;
import com.opengamma.financial.security.FinancialSecurityVisitor;
import com.opengamma.financial.security.FinancialSecurityVisitorAdapter;
import com.opengamma.financial.security.fx.FXForwardSecurity;
import com.opengamma.financial.security.fx.NonDeliverableFXForwardSecurity;
import com.opengamma.master.historicaltimeseries.HistoricalTimeSeriesResolver;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.async.AsynchronousExecution;
import com.opengamma.util.money.Currency;
import com.opengamma.util.time.DateUtils;
import com.opengamma.util.time.Tenor;
/**
*
*/
public abstract class FXForwardPointsFunction extends AbstractFunction {
/** The logger */
private static final Logger s_logger = LoggerFactory.getLogger(FXForwardPointsFunction.class);
/** The value requirements */
private final String[] _valueRequirements;
/**
* @param valueRequirements The value requirement names, not null
*/
public FXForwardPointsFunction(final String... valueRequirements) {
ArgumentChecker.notNull(valueRequirements, "value requirements");
_valueRequirements = valueRequirements;
}
/**
* Constructs an object capable of converting from {@link ComputationTarget} to {@link InstrumentDefinition}.
* @param context The compilation context, not null
* @return The converter
*/
protected TradeConverter getTargetToDefinitionConverter(final FunctionCompilationContext context) {
final SecuritySource securitySource = OpenGammaCompilationContext.getSecuritySource(context);
final HolidaySource holidaySource = OpenGammaCompilationContext.getHolidaySource(context);
final RegionSource regionSource = OpenGammaCompilationContext.getRegionSource(context);
final ConventionBundleSource conventionBundleSource = OpenGammaCompilationContext.getConventionBundleSource(context);
final ConventionSource conventionSource = OpenGammaCompilationContext.getConventionSource(context);
final FXForwardSecurityConverter fxForwardSecurityConverter = new FXForwardSecurityConverter();
final NonDeliverableFXForwardSecurityConverter nonDeliverableFXForwardSecurityConverter = new NonDeliverableFXForwardSecurityConverter();
final FinancialSecurityVisitor<InstrumentDefinition<?>> securityConverter = FinancialSecurityVisitorAdapter.<InstrumentDefinition<?>>builder()
.fxForwardVisitor(fxForwardSecurityConverter)
.nonDeliverableFxForwardVisitor(nonDeliverableFXForwardSecurityConverter)
.create();
final FutureTradeConverter futureTradeConverter = new FutureTradeConverter(securitySource, holidaySource, conventionSource, conventionBundleSource,
regionSource);
return new TradeConverter(futureTradeConverter, securityConverter);
}
/**
* Constructs an object capable of converting from {@link InstrumentDefinition} to {@link InstrumentDerivative}.
* @param context The compilation context, not null
* @return The converter
*/
protected FixedIncomeConverterDataProvider getDefinitionToDerivativeConverter(final FunctionCompilationContext context) {
final ConventionBundleSource conventionBundleSource = OpenGammaCompilationContext.getConventionBundleSource(context);
final HistoricalTimeSeriesResolver timeSeriesResolver = OpenGammaCompilationContext.getHistoricalTimeSeriesResolver(context);
return new FixedIncomeConverterDataProvider(conventionBundleSource, timeSeriesResolver);
}
/**
* Base compiled function for all FX forward points pricing and risk functions.
*/
public abstract class FXForwardPointsCompiledFunction extends AbstractInvokingCompiledFunction {
/** Converts targets to definitions */
private final TradeConverter _tradeToDefinitionConverter;
/** Converts definitions to derivatives */
private final FixedIncomeConverterDataProvider _definitionToDerivativeConverter;
/** Indicates whether the results set {@link ValuePropertyNames#CURRENCY} */
private final boolean _withCurrency;
/**
* @param tradeToDefinitionConverter Converts trades to definitions, not null
* @param definitionToDerivativeConverter Converts definitions to derivatives, not null
* @param withCurrency True if this function sets {@link ValuePropertyNames#CURRENCY}
*/
protected FXForwardPointsCompiledFunction(final TradeConverter tradeToDefinitionConverter,
final FixedIncomeConverterDataProvider definitionToDerivativeConverter, final boolean withCurrency) {
ArgumentChecker.notNull(tradeToDefinitionConverter, "target to definition converter");
ArgumentChecker.notNull(definitionToDerivativeConverter, "definition to derivative converter");
_tradeToDefinitionConverter = tradeToDefinitionConverter;
_definitionToDerivativeConverter = definitionToDerivativeConverter;
_withCurrency = withCurrency;
}
@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);
final HistoricalTimeSeriesBundle timeSeries = HistoricalTimeSeriesFunctionUtils.getHistoricalTimeSeriesInputs(executionContext, inputs);
final InstrumentDefinition<?> definition = getDefinitionFromTarget(target);
final Forex forex = getForex(target, now, timeSeries, definition);
final FXMatrix fxMatrix = new FXMatrix();
final CurrencyPairs pairs = (CurrencyPairs) inputs.getValue(CURRENCY_PAIRS);
final Currency currency1 = forex.getCurrency1();
final Currency currency2 = forex.getCurrency2();
if (pairs.getCurrencyPair(currency1, currency2).getBase().equals(currency1)) {
final double spotRate = (Double) inputs.getValue(new ValueRequirement(ValueRequirementNames.SPOT_RATE,
CurrencyPair.TYPE.specification(CurrencyPair.of(currency2, currency1))));
fxMatrix.addCurrency(currency1, currency2, spotRate);
} else {
final double spotRate = (Double) inputs.getValue(new ValueRequirement(ValueRequirementNames.SPOT_RATE,
CurrencyPair.TYPE.specification(CurrencyPair.of(currency2, currency1))));
fxMatrix.addCurrency(currency2, currency1, 1 / spotRate);
}
return getValues(inputs, target, desiredValues, forex, fxMatrix, now);
}
@Override
public ComputationTargetType getTargetType() {
return ComputationTargetType.TRADE;
}
@Override
public boolean canApplyTo(final FunctionCompilationContext context, final ComputationTarget target) {
final Security security = target.getTrade().getSecurity();
return security instanceof FXForwardSecurity ||
security instanceof NonDeliverableFXForwardSecurity;
}
@Override
public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) {
final ValueProperties properties = getResultProperties(target).get();
final Set<ValueSpecification> results = new HashSet<>();
for (final String valueRequirement : _valueRequirements) {
results.add(new ValueSpecification(valueRequirement, target.toSpecification(), properties));
}
return results;
}
@Override
public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) {
final ValueProperties constraints = desiredValue.getConstraints();
final Set<String> curveExposureConfigs = constraints.getValues(CURVE_EXPOSURES);
if (curveExposureConfigs == null) {
return null;
}
final Set<String> fxForwardCurveNames = constraints.getValues(FORWARD_CURVE_NAME);
if (fxForwardCurveNames == null || fxForwardCurveNames.size() != 1) {
return null;
}
try {
final FinancialSecurity security = (FinancialSecurity) target.getTrade().getSecurity();
final ConfigSource configSource = OpenGammaCompilationContext.getConfigSource(context);
final SecuritySource securitySource = OpenGammaCompilationContext.getSecuritySource(context);
final ConfigDBInstrumentExposuresProvider exposureSource = new ConfigDBInstrumentExposuresProvider(configSource, securitySource);
final ConfigDBCurveConstructionConfigurationSource constructionConfigurationSource = new ConfigDBCurveConstructionConfigurationSource(configSource);
final Set<ValueRequirement> requirements = new HashSet<>();
for (final String curveExposureConfig : curveExposureConfigs) {
final Set<String> curveConstructionConfigurationNames = exposureSource.getCurveConstructionConfigurationsForConfig(curveExposureConfig, security);
for (final String curveConstructionConfigurationName : curveConstructionConfigurationNames) {
final ValueProperties properties = ValueProperties.with(CURVE_CONSTRUCTION_CONFIG, curveConstructionConfigurationName).get();
requirements.add(new ValueRequirement(CURVE_BUNDLE, ComputationTargetSpecification.NULL, properties));
requirements.add(new ValueRequirement(JACOBIAN_BUNDLE, ComputationTargetSpecification.NULL, properties));
final CurveConstructionConfiguration curveConstructionConfiguration = constructionConfigurationSource.getCurveConstructionConfiguration(curveConstructionConfigurationName);
final String[] curveNames = CurveUtils.getCurveNamesForConstructionConfiguration(curveConstructionConfiguration);
for (final String curveName : curveNames) {
final ValueProperties curveProperties = ValueProperties.builder()
.with(CURVE, curveName)
.get();
requirements.add(new ValueRequirement(CURVE_DEFINITION, ComputationTargetSpecification.NULL, curveProperties));
requirements.add(new ValueRequirement(FX_MATRIX, ComputationTargetSpecification.NULL, properties));
}
}
}
final Collection<Currency> currencies = FinancialSecurityUtils.getCurrencies(security, securitySource);
if (currencies.size() > 1) {
final Iterator<Currency> iter = currencies.iterator();
final Currency initialCurrency = iter.next();
while (iter.hasNext()) {
requirements.add(new ValueRequirement(ValueRequirementNames.SPOT_RATE, CurrencyPair.TYPE.specification(CurrencyPair.of(iter.next(), initialCurrency))));
}
}
final InstrumentDefinition<?> definition = getDefinitionFromTarget(target);
final Set<ValueRequirement> timeSeriesRequirements = getConversionTimeSeriesRequirements(context, target, definition);
if (timeSeriesRequirements == null) {
return null;
}
requirements.addAll(timeSeriesRequirements);
final String fxForwardCurveName = Iterables.getOnlyElement(fxForwardCurveNames);
final ValueProperties fxForwardCurveProperties = ValueProperties.builder().with(CURVE, fxForwardCurveName).get();
final ValueRequirement fxForwardCurveDefinition = new ValueRequirement(CURVE_DEFINITION, ComputationTargetSpecification.NULL,
fxForwardCurveProperties);
final ValueRequirement fxForwardCurveSpecification = new ValueRequirement(CURVE_SPECIFICATION, ComputationTargetSpecification.NULL,
fxForwardCurveProperties);
final ValueRequirement fxForwardCurveDataRequirement = new ValueRequirement(CURVE_MARKET_DATA, ComputationTargetSpecification.NULL,
fxForwardCurveProperties);
final ValueRequirement currencyPairsRequirement = new ValueRequirement(CURRENCY_PAIRS, ComputationTargetSpecification.NULL, ValueProperties.none());
requirements.add(fxForwardCurveDataRequirement);
requirements.add(fxForwardCurveDefinition);
requirements.add(fxForwardCurveSpecification);
requirements.add(currencyPairsRequirement);
return requirements;
} catch (final Exception e) {
s_logger.error(e.getMessage());
return null;
}
}
protected ValueProperties.Builder getResultProperties(final ComputationTarget target) {
final ValueProperties.Builder properties = createValueProperties()
.withAny(CURVE_EXPOSURES)
.withAny(FORWARD_CURVE_NAME)
.with(PROPERTY_CURVE_TYPE, FORWARD_POINTS);
if (_withCurrency) {
properties.with(CURRENCY, ((FinancialSecurity) target.getTrade().getSecurity()).accept(ForexVisitors.getReceiveCurrencyVisitor()).getCode());
}
return properties;
}
/**
* Gets an {@link InstrumentDefinition} given a target.
* @param target The target, not null
* @return An instrument definition
*/
protected InstrumentDefinition<?> getDefinitionFromTarget(final ComputationTarget target) {
return _tradeToDefinitionConverter.convert(target.getTrade());
}
/**
* Gets a conversion time-series for an instrument definition. If no time-series are required,
* returns an empty set.
* @param context The compilation context, not null
* @param target The target, not null
* @param definition The definition, not null
* @return A set of time-series requirements
*/
protected Set<ValueRequirement> getConversionTimeSeriesRequirements(final FunctionCompilationContext context, final ComputationTarget target,
final InstrumentDefinition<?> definition) {
return _definitionToDerivativeConverter.getConversionTimeSeriesRequirements(target.getTrade().getSecurity(), definition);
}
/**
* Gets an {@link InstrumentDerivative}.
* @param target The target, not null
* @param now The valuation time, not null
* @param timeSeries The conversion time series bundle, not null but may be empty
* @param definition The definition, not null
* @return The instrument derivative
*/
protected Forex getForex(final ComputationTarget target, final ZonedDateTime now, final HistoricalTimeSeriesBundle timeSeries,
final InstrumentDefinition<?> definition) {
return (Forex) _definitionToDerivativeConverter.convert(target.getTrade().getSecurity(), definition, now, timeSeries);
}
/**
* Calculates the result.
* @param inputs The inputs, not null
* @param target The target, not null
* @param desiredValues The desired values for this function, not null
* @param forex The forex trade, not null
* @param fxMatrix The FX matrix, not null
* @param now The valuation time, not null
* @return The results
*/
protected abstract Set<ComputedValue> getValues(FunctionInputs inputs, ComputationTarget target, Set<ValueRequirement> desiredValues,
Forex forex, FXMatrix fxMatrix, ZonedDateTime now);
protected MulticurveProviderDiscount getMergedProviders(final FunctionInputs inputs, final FXMatrix matrix) {
final Collection<MulticurveProviderDiscount> providers = new HashSet<>();
for (final ComputedValue input : inputs.getAllValues()) {
final String valueName = input.getSpecification().getValueName();
if (CURVE_BUNDLE.equals(valueName)) {
providers.add((MulticurveProviderDiscount) input.getValue());
}
}
final MulticurveProviderDiscount result = ProviderUtils.mergeDiscountingProviders(providers);
return ProviderUtils.mergeDiscountingProviders(result, matrix);
}
protected CurveBuildingBlockBundle getMergedCurveBuildingBlocks(final FunctionInputs inputs) {
final CurveBuildingBlockBundle result = new CurveBuildingBlockBundle();
for (final ComputedValue input : inputs.getAllValues()) {
final String valueName = input.getSpecification().getValueName();
if (valueName.equals(JACOBIAN_BUNDLE)) {
result.addAll((CurveBuildingBlockBundle) input.getValue());
}
}
return result;
}
protected DoublesCurve getForwardPoints(final FunctionInputs inputs, final String fxForwardCurveName,
final ZonedDateTime now) {
final ValueProperties curveProperties = ValueProperties.with(CURVE, fxForwardCurveName).get();
final ValueRequirement definitionRequirement = new ValueRequirement(CURVE_DEFINITION, ComputationTargetSpecification.NULL, curveProperties);
final CurveDefinition definition = (CurveDefinition) inputs.getValue(definitionRequirement);
if (definition == null) {
throw new OpenGammaRuntimeException("Could not get definition for " + fxForwardCurveName);
}
final String interpolatorName, leftExtrapolatorName, rightExtrapolatorName;
if (definition instanceof InterpolatedCurveDefinition) {
final InterpolatedCurveDefinition interpolatedDefinition = (InterpolatedCurveDefinition) definition;
interpolatorName = interpolatedDefinition.getInterpolatorName();
if (interpolatedDefinition.getLeftExtrapolatorName() != null) {
leftExtrapolatorName = interpolatedDefinition.getLeftExtrapolatorName();
rightExtrapolatorName = interpolatedDefinition.getRightExtrapolatorName();
} else {
leftExtrapolatorName = Interpolator1DFactory.LINEAR_EXTRAPOLATOR;
rightExtrapolatorName = Interpolator1DFactory.LINEAR_EXTRAPOLATOR;
}
} else {
interpolatorName = Interpolator1DFactory.LINEAR;
leftExtrapolatorName = Interpolator1DFactory.LINEAR_EXTRAPOLATOR;
rightExtrapolatorName = Interpolator1DFactory.LINEAR_EXTRAPOLATOR;
}
final CurveSpecification specification = (CurveSpecification) inputs.getValue(CURVE_SPECIFICATION);
final SnapshotDataBundle data = (SnapshotDataBundle) inputs.getValue(CURVE_MARKET_DATA);
final DoubleArrayList tList = new DoubleArrayList();
final DoubleArrayList fxList = new DoubleArrayList();
for (final CurveNodeWithIdentifier nodeWithId : specification.getNodes()) {
final CurveNode node = nodeWithId.getCurveNode();
if (!(node instanceof FXForwardNode)) {
throw new OpenGammaRuntimeException("Unexpected node " + nodeWithId + " found");
}
final Double fxForward = data.getDataPoint(nodeWithId.getIdentifier());
if (fxForward == null) {
throw new OpenGammaRuntimeException("Could not get FX forward rate for " + node);
}
final Tenor tenor = node.getResolvedMaturity();
tList.add(DateUtils.getDifferenceInYears(now, now.plus(tenor.getPeriod())));
fxList.add(fxForward);
}
final Interpolator1D interpolator = CombinedInterpolatorExtrapolatorFactory.getInterpolator(interpolatorName,
leftExtrapolatorName, rightExtrapolatorName);
return InterpolatedDoublesCurve.from(tList.toDoubleArray(), fxList.toDoubleArray(), interpolator);
}
}
}