/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.analytics.volatility.surface;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.threeten.bp.Instant;
import org.threeten.bp.LocalDate;
import org.threeten.bp.LocalTime;
import org.threeten.bp.ZoneOffset;
import org.threeten.bp.ZonedDateTime;
import com.google.common.collect.Iterables;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.core.marketdatasnapshot.VolatilitySurfaceData;
import com.opengamma.engine.ComputationTarget;
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.SurfaceAndCubePropertyNames;
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.analytics.model.InstrumentTypeProperties;
import com.opengamma.id.ExternalId;
import com.opengamma.id.VersionCorrection;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.tuple.Pair;
import com.opengamma.util.tuple.Pairs;
/**
* Gets volatility surface data from definitions and specifications. No financial modelling is done and the data points can be any type of data (e.g. price, implied lognormal volatility).
*/
public abstract class RawVolatilitySurfaceDataFunction extends AbstractFunction {
/** The logger */
private static final Logger s_logger = LoggerFactory.getLogger(RawVolatilitySurfaceDataFunction.class);
/**
* Value specification property for the surface result. This allows surface to be distinguished by instrument type (e.g. an FX volatility surface, swaption ATM volatility surface).
*/
private final String _instrumentType;
private ConfigDBVolatilitySurfaceDefinitionSource _volatilitySurfaceDefinitionSource;
private ConfigDBVolatilitySurfaceSpecificationSource _volatilitySurfaceSpecificationSource;
/**
* @param instrumentType The instrument type, not null
*/
public RawVolatilitySurfaceDataFunction(final String instrumentType) {
ArgumentChecker.notNull(instrumentType, "Instrument Type");
_instrumentType = instrumentType;
}
@Override
public void init(final FunctionCompilationContext context) {
_volatilitySurfaceDefinitionSource = ConfigDBVolatilitySurfaceDefinitionSource.init(context, this);
_volatilitySurfaceSpecificationSource = ConfigDBVolatilitySurfaceSpecificationSource.init(context, this);
}
/**
* Gets the target type for the surface
*
* @return The target type
*/
protected abstract ComputationTargetType getTargetType();
/**
* Determines whether this function applies to the target
*
* @param context The compilation context
* @param target The computation target
* @return true if this function applies to the target
*/
protected boolean canApplyTo(final FunctionCompilationContext context, final ComputationTarget target) {
return true;
}
/**
* Gets a volatility surface definition from a name, target and instrument type. The full name of the surface is constructed as
* <p>
* [SURFACE NAME]_[TARGET NAME]_[INSTRUMENT TYPE]
* <p>
*
* @param definitionSource The surface definition source
* @param versionCorrection The version/correction timestamp
* @param target The computation target
* @param definitionName The definition name
* @return The volatility surface definition
* @throws OpenGammaRuntimeException if a volatility surface definition with the full name is not found
*/
protected abstract VolatilitySurfaceDefinition<?, ?> getDefinition(VolatilitySurfaceDefinitionSource definitionSource, VersionCorrection versionCorrection, ComputationTarget target,
String definitionName);
/**
* Gets a volatility surface specification from a name, target and instrument type. The full name of the surface is constructed as
* <p>
* [SURFACE NAME]_[TRIMMED TARGET]_[INSTRUMENT TYPE]
* <p>
*
* @param specificationSource The surface specification source
* @param versionCorrection The version/correction timestamp
* @param target The computation target
* @param specificationName The specification name
* @return The volatility surface specification
* @throws OpenGammaRuntimeException if a volatility surface specification with the full name is not found
*/
protected abstract VolatilitySurfaceSpecification getSpecification(VolatilitySurfaceSpecificationSource specificationSource, VersionCorrection versionCorrection, ComputationTarget target,
String specificationName);
/**
* @return The instrument type of the surface
*/
protected String getInstrumentType() {
return _instrumentType;
}
/**
* Uses the volatility surface definition and specification to work out which market data requirements are needed to construct the surface with the given name and type.
*
* @param <X> The type of the x-axis data
* @param <Y> The type of the y-axis data
* @param specification The volatility specification
* @param definition The volatility definition
* @param atInstant The time stamp of the surface
* @param surfaceName The surface name
* @param instrumentType The instrument type
* @return A set of market data value requirements, or null if the volatility surface specification or definition is null
*/
public static <X, Y> Set<ValueRequirement> buildDataRequirements(final VolatilitySurfaceSpecification specification, final VolatilitySurfaceDefinition<X, Y> definition,
final ZonedDateTime atInstant, final String surfaceName, final String instrumentType) {
if (specification == null) {
s_logger.error("Volatility surface specification called {} for instrument type {} was null", surfaceName, instrumentType);
return null;
}
if (definition == null) {
s_logger.error("Volatility surface definition called {} for instrument type {} was null", surfaceName, instrumentType);
return null;
}
final Set<ValueRequirement> result = new HashSet<>();
final SurfaceInstrumentProvider<X, Y> provider = (SurfaceInstrumentProvider<X, Y>) specification.getSurfaceInstrumentProvider();
for (final X x : definition.getXs()) {
for (final Y y : definition.getYs()) {
final ExternalId identifier = provider.getInstrument(x, y, atInstant.toLocalDate());
result.add(new ValueRequirement(provider.getDataFieldName(), ComputationTargetType.PRIMITIVE, identifier));
}
}
return result;
}
@Override
public CompiledFunctionDefinition compile(final FunctionCompilationContext myContext, final Instant atInstant) {
final ZonedDateTime atZDT = ZonedDateTime.ofInstant(atInstant, ZoneOffset.UTC);
return new CompiledFunction(atZDT.with(LocalTime.MIDNIGHT), atZDT.plusDays(1).with(LocalTime.MIDNIGHT).minusNanos(1000000), atZDT, _volatilitySurfaceDefinitionSource,
_volatilitySurfaceSpecificationSource);
}
/**
* Implementation of the compiled function
*/
protected class CompiledFunction extends AbstractInvokingCompiledFunction {
/** The valuation time */
private final ZonedDateTime _now;
/** Source for volatility surface definitions */
private final ConfigDBVolatilitySurfaceDefinitionSource _definitionSource;
/** Source for volatility surface specifications */
private final ConfigDBVolatilitySurfaceSpecificationSource _specificationSource;
/**
* @param from Earliest time that the invoker is valid
* @param to Latest time that the invoker is valid
* @param now The valuation time
* @param definitionSource The volatility surface definition source
* @param specificationSource The volatility surface specification source
*/
public CompiledFunction(final ZonedDateTime from, final ZonedDateTime to, final ZonedDateTime now, final ConfigDBVolatilitySurfaceDefinitionSource definitionSource,
final ConfigDBVolatilitySurfaceSpecificationSource specificationSource) {
super(from.toInstant(), to.toInstant());
_now = now;
_definitionSource = definitionSource;
_specificationSource = specificationSource;
}
@SuppressWarnings("synthetic-access")
@Override
public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) {
return Collections.singleton(new ValueSpecification(ValueRequirementNames.VOLATILITY_SURFACE_DATA, target.toSpecification(), createValueProperties()
.withAny(ValuePropertyNames.SURFACE).with(InstrumentTypeProperties.PROPERTY_SURFACE_INSTRUMENT_TYPE, _instrumentType)
.withAny(SurfaceAndCubePropertyNames.PROPERTY_SURFACE_QUOTE_TYPE).withAny(SurfaceAndCubePropertyNames.PROPERTY_SURFACE_UNITS).get()));
}
@SuppressWarnings("synthetic-access")
@Override
public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) {
// REVIEW 2013-11-06 Andrew -- This logic with the instrument type is not necessary - see getResults
final String instrumentType = desiredValue.getConstraints().getStrictValue(InstrumentTypeProperties.PROPERTY_SURFACE_INSTRUMENT_TYPE);
if (instrumentType == null) {
return null;
}
if (!_instrumentType.equals(instrumentType)) {
s_logger.error("Instrument type {} did not match that required {}", instrumentType, _instrumentType);
return null;
}
final String surfaceName = desiredValue.getConstraints().getStrictValue(ValuePropertyNames.SURFACE);
if (surfaceName == null) {
return null;
}
try {
final VolatilitySurfaceDefinition<?, ?> definition = getDefinition(_definitionSource, context.getComputationTargetResolver().getVersionCorrection(), target, surfaceName);
if (definition == null) {
s_logger.error("Could not get volatility surface definition for instrument type {} with target {} called {}", new Object[] {target, _instrumentType, surfaceName });
return null;
}
final VolatilitySurfaceSpecification specification = getSpecification(_specificationSource, context.getComputationTargetResolver().getVersionCorrection(), target, surfaceName);
if (specification == null) {
s_logger.error("Could not get volatility surface specification for instrument type {} with target {} called {}", new Object[] {target, _instrumentType, surfaceName });
return null;
}
final Set<ValueRequirement> requirements = buildDataRequirements(specification, definition, _now, surfaceName, instrumentType);
final ValueProperties definitionProperties = ValueProperties.builder().with(ValuePropertyNames.SURFACE, surfaceName)
.with(InstrumentTypeProperties.PROPERTY_SURFACE_INSTRUMENT_TYPE, instrumentType).get();
final ValueProperties specificationProperties = ValueProperties.builder().with(ValuePropertyNames.SURFACE, surfaceName)
.with(InstrumentTypeProperties.PROPERTY_SURFACE_INSTRUMENT_TYPE, instrumentType)
.with(SurfaceAndCubePropertyNames.PROPERTY_SURFACE_QUOTE_TYPE, specification.getSurfaceQuoteType())
.with(SurfaceAndCubePropertyNames.PROPERTY_SURFACE_UNITS, specification.getQuoteUnits()).get();
requirements.add(new ValueRequirement(ValueRequirementNames.VOLATILITY_SURFACE_SPEC, target.toSpecification(), specificationProperties));
requirements.add(new ValueRequirement(ValueRequirementNames.VOLATILITY_SURFACE_DEFINITION, target.toSpecification(), definitionProperties));
return requirements;
} catch (final Exception e) {
s_logger.error(e.getMessage());
return null;
}
}
@SuppressWarnings("synthetic-access")
@Override
public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target, final Map<ValueSpecification, ValueRequirement> inputs) {
String surfaceQuoteType = null;
String surfaceQuoteUnits = null;
String surfaceName = null;
for (final Map.Entry<ValueSpecification, ValueRequirement> entry : inputs.entrySet()) {
final ValueSpecification key = entry.getKey();
if (key.getValueName().equals(ValueRequirementNames.VOLATILITY_SURFACE_SPEC)) {
surfaceQuoteType = key.getProperty(SurfaceAndCubePropertyNames.PROPERTY_SURFACE_QUOTE_TYPE);
surfaceQuoteUnits = key.getProperty(SurfaceAndCubePropertyNames.PROPERTY_SURFACE_UNITS);
surfaceName = key.getProperty(ValuePropertyNames.SURFACE);
break;
}
}
if (surfaceName == null) {
return null;
}
assert surfaceQuoteType != null;
assert surfaceQuoteUnits != null;
return Collections.singleton(new ValueSpecification(ValueRequirementNames.VOLATILITY_SURFACE_DATA, target.toSpecification(), createValueProperties()
.with(ValuePropertyNames.SURFACE, surfaceName).with(InstrumentTypeProperties.PROPERTY_SURFACE_INSTRUMENT_TYPE, _instrumentType)
.with(SurfaceAndCubePropertyNames.PROPERTY_SURFACE_QUOTE_TYPE, surfaceQuoteType).with(SurfaceAndCubePropertyNames.PROPERTY_SURFACE_UNITS, surfaceQuoteUnits).get()));
}
@Override
public ComputationTargetType getTargetType() {
return RawVolatilitySurfaceDataFunction.this.getTargetType();
}
@Override
public boolean canApplyTo(final FunctionCompilationContext context, final ComputationTarget target) {
return RawVolatilitySurfaceDataFunction.this.canApplyTo(context, target);
}
@SuppressWarnings({"synthetic-access" })
@Override
public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) {
final ValueRequirement desiredValue = Iterables.getOnlyElement(desiredValues);
final String surfaceName = desiredValue.getConstraint(ValuePropertyNames.SURFACE);
final String instrumentType = desiredValue.getConstraint(InstrumentTypeProperties.PROPERTY_SURFACE_INSTRUMENT_TYPE);
final Object definitionObject = inputs.getValue(ValueRequirementNames.VOLATILITY_SURFACE_DEFINITION);
if (definitionObject == null) {
throw new OpenGammaRuntimeException("Could not get volatility surface definition");
}
final Object specificationObject = inputs.getValue(ValueRequirementNames.VOLATILITY_SURFACE_SPEC);
if (specificationObject == null) {
throw new OpenGammaRuntimeException("Could not get volatility surface specification");
}
@SuppressWarnings("unchecked")
final VolatilitySurfaceDefinition<Object, Object> definition = (VolatilitySurfaceDefinition<Object, Object>) definitionObject;
final VolatilitySurfaceSpecification specification = (VolatilitySurfaceSpecification) specificationObject;
final LocalDate valuationDate = LocalDate.now(executionContext.getValuationClock());
final SurfaceInstrumentProvider<Object, Object> provider = (SurfaceInstrumentProvider<Object, Object>) specification.getSurfaceInstrumentProvider();
final Map<Pair<Object, Object>, Double> volatilityValues = new HashMap<>();
final ObjectArrayList<Object> xList = new ObjectArrayList<>();
final ObjectArrayList<Object> yList = new ObjectArrayList<>();
for (final Object x : definition.getXs()) {
for (final Object y : definition.getYs()) {
final ExternalId identifier = provider.getInstrument(x, y, valuationDate);
final ValueRequirement requirement = new ValueRequirement(provider.getDataFieldName(), ComputationTargetType.PRIMITIVE, identifier);
final Double volatility = (Double) inputs.getValue(requirement);
if (volatility != null) {
xList.add(x);
yList.add(y);
volatilityValues.put(Pairs.of(x, y), volatility);
} else {
s_logger.info("Missing value {}", identifier.toString());
}
}
}
final VolatilitySurfaceData<Object, Object> volSurfaceData = new VolatilitySurfaceData<>(definition.getName(), specification.getName(), definition.getTarget(), definition.getXs(),
definition.getYs(), volatilityValues);
final ValueSpecification result = new ValueSpecification(ValueRequirementNames.VOLATILITY_SURFACE_DATA, target.toSpecification(), createValueProperties()
.with(ValuePropertyNames.SURFACE, surfaceName).with(InstrumentTypeProperties.PROPERTY_SURFACE_INSTRUMENT_TYPE, instrumentType)
.with(SurfaceAndCubePropertyNames.PROPERTY_SURFACE_QUOTE_TYPE, specification.getSurfaceQuoteType())
.with(SurfaceAndCubePropertyNames.PROPERTY_SURFACE_UNITS, specification.getQuoteUnits()).get());
return Collections.singleton(new ComputedValue(result, volSurfaceData));
}
@Override
public boolean canHandleMissingInputs() {
return true;
}
@Override
public boolean canHandleMissingRequirements() {
return true;
}
/**
* Gets the definition source.
*
* @return The definition source
*/
protected ConfigDBVolatilitySurfaceDefinitionSource getDefinitionSource() {
return _definitionSource;
}
/**
* Gets the specification source.
*
* @return The specification source
*/
protected ConfigDBVolatilitySurfaceSpecificationSource getSpecificationSource() {
return _specificationSource;
}
};
}