/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.analytics.model.volatility.surface.black; import static com.opengamma.engine.value.ValuePropertyNames.CURVE; import static com.opengamma.engine.value.ValuePropertyNames.SURFACE; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.INTEGRATED_VARIANCE; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.LINEAR_TIME; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.LINEAR_Y; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.LOG_TIME; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.LOG_Y; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.MIXED_LOG_NORMAL; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.PROPERTY_MIXED_LOG_NORMAL_WEIGHTING_FUNCTION; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.PROPERTY_SABR_EXTERNAL_BETA; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.PROPERTY_SABR_MODEL; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.PROPERTY_SABR_USE_EXTERNAL_BETA; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.PROPERTY_SABR_WEIGHTING_FUNCTION; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.PROPERTY_SMILE_INTERPOLATOR; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.PROPERTY_SPLINE_EXTRAPOLATOR_FAILURE; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.PROPERTY_SPLINE_INTERPOLATOR; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.PROPERTY_SPLINE_LEFT_EXTRAPOLATOR; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.PROPERTY_SPLINE_RIGHT_EXTRAPOLATOR; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.PROPERTY_TIME_AXIS; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.PROPERTY_TIME_INTERPOLATOR; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.PROPERTY_TIME_LEFT_EXTRAPOLATOR; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.PROPERTY_TIME_RIGHT_EXTRAPOLATOR; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.PROPERTY_VOLATILITY_TRANSFORM; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.PROPERTY_Y_AXIS; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.SABR; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.SPLINE; import static com.opengamma.financial.analytics.model.volatility.surface.black.BlackVolatilitySurfacePropertyNamesAndValues.VARIANCE; import java.util.Collections; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Iterables; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.engine.target.ComputationTargetType; 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.financial.analytics.model.InstrumentTypeProperties; import com.opengamma.financial.analytics.model.curve.forward.ForwardCurveValuePropertyNames; import com.opengamma.id.ExternalId; /** * */ public class BlackVolatilitySurfacePropertyUtils { private static final Logger s_logger = LoggerFactory.getLogger(BlackVolatilitySurfacePropertyUtils.class); static boolean useLogTime(final String property) { if (property.equals(LOG_TIME)) { return true; } if (property.equals(LINEAR_TIME)) { return false; } throw new OpenGammaRuntimeException("Could not recognise time axis property " + property); } static boolean useIntegratedVariance(final String property) { if (property.equals(INTEGRATED_VARIANCE)) { return true; } if (property.equals(VARIANCE)) { return false; } throw new OpenGammaRuntimeException("Could not recognise volatility transform property " + property); } static boolean useLogYAxis(final String property) { if (property.equals(LOG_Y)) { return true; } if (property.equals(LINEAR_Y)) { return false; } throw new OpenGammaRuntimeException("Could not recognise y-axis property type " + property); } public static ValueProperties.Builder addCommonVolatilityInterpolatorProperties(final ValueProperties properties, final String smileInterpolator) { return properties.copy() .withAny(PROPERTY_TIME_AXIS) .withAny(PROPERTY_Y_AXIS) .withAny(PROPERTY_VOLATILITY_TRANSFORM) .withAny(PROPERTY_TIME_INTERPOLATOR) .withAny(PROPERTY_TIME_LEFT_EXTRAPOLATOR) .withAny(PROPERTY_TIME_RIGHT_EXTRAPOLATOR) .with(ValuePropertyNames.SURFACE_CALCULATION_METHOD, BlackVolatilitySurfacePropertyNamesAndValues.INTERPOLATED_BLACK_LOGNORMAL) .with(PROPERTY_SMILE_INTERPOLATOR, smileInterpolator); } public static ValueProperties.Builder addCommonVolatilityInterpolatorProperties(final ValueProperties properties, final ValueRequirement desiredValue, final String smileInterpolator) { final String timeAxis = desiredValue.getConstraint(PROPERTY_TIME_AXIS); final String yAxis = desiredValue.getConstraint(PROPERTY_Y_AXIS); final String volatilityTransform = desiredValue.getConstraint(PROPERTY_VOLATILITY_TRANSFORM); final String timeInterpolator = desiredValue.getConstraint(PROPERTY_TIME_INTERPOLATOR); final String timeLeftExtrapolator = desiredValue.getConstraint(PROPERTY_TIME_LEFT_EXTRAPOLATOR); final String timeRightExtrapolator = desiredValue.getConstraint(PROPERTY_TIME_RIGHT_EXTRAPOLATOR); return properties.copy() .with(PROPERTY_SMILE_INTERPOLATOR, smileInterpolator) .with(ValuePropertyNames.SURFACE_CALCULATION_METHOD, BlackVolatilitySurfacePropertyNamesAndValues.INTERPOLATED_BLACK_LOGNORMAL) .with(PROPERTY_TIME_AXIS, timeAxis) .with(PROPERTY_Y_AXIS, yAxis) .with(PROPERTY_VOLATILITY_TRANSFORM, volatilityTransform) .with(PROPERTY_TIME_INTERPOLATOR, timeInterpolator) .with(PROPERTY_TIME_LEFT_EXTRAPOLATOR, timeLeftExtrapolator) .with(PROPERTY_TIME_RIGHT_EXTRAPOLATOR, timeRightExtrapolator); } public static Set<ValueRequirement> ensureCommonVolatilityInterpolatorProperties(final ValueProperties constraints) { final Set<String> timeAxisNames = constraints.getValues(PROPERTY_TIME_AXIS); if (timeAxisNames == null || timeAxisNames.size() != 1) { return null; } final Set<String> yAxisNames = constraints.getValues(PROPERTY_Y_AXIS); if (yAxisNames == null || yAxisNames.size() != 1) { return null; } final Set<String> volatilityTransformNames = constraints.getValues(PROPERTY_VOLATILITY_TRANSFORM); if (volatilityTransformNames == null || volatilityTransformNames.size() != 1) { return null; } final Set<String> timeInterpolatorNames = constraints.getValues(PROPERTY_TIME_INTERPOLATOR); if (timeInterpolatorNames == null || timeInterpolatorNames.size() != 1) { return null; } final Set<String> timeLeftExtrapolatorNames = constraints.getValues(PROPERTY_TIME_LEFT_EXTRAPOLATOR); if (timeLeftExtrapolatorNames == null || timeLeftExtrapolatorNames.size() != 1) { return null; } final Set<String> timeRightExtrapolatorNames = constraints.getValues(PROPERTY_TIME_RIGHT_EXTRAPOLATOR); if (timeRightExtrapolatorNames == null || timeRightExtrapolatorNames.size() != 1) { return null; } return Collections.emptySet(); } public static ValueProperties.Builder addSplineVolatilityInterpolatorProperties(final ValueProperties properties) { return addCommonVolatilityInterpolatorProperties(properties.copy() .withAny(PROPERTY_SPLINE_INTERPOLATOR) .withAny(PROPERTY_SPLINE_LEFT_EXTRAPOLATOR) .withAny(PROPERTY_SPLINE_RIGHT_EXTRAPOLATOR) .withAny(PROPERTY_SPLINE_EXTRAPOLATOR_FAILURE) .get(), SPLINE); } public static ValueProperties.Builder addSplineVolatilityInterpolatorProperties(final ValueProperties properties, final ValueRequirement desiredValue) { final String interpolatorName = desiredValue.getConstraint(PROPERTY_SPLINE_INTERPOLATOR); final String leftExtrapolatorName = desiredValue.getConstraint(PROPERTY_SPLINE_LEFT_EXTRAPOLATOR); final String rightExtrapolatorName = desiredValue.getConstraint(PROPERTY_SPLINE_RIGHT_EXTRAPOLATOR); final String extrapolatorFailureBehaviour = desiredValue.getConstraint(PROPERTY_SPLINE_EXTRAPOLATOR_FAILURE); return addCommonVolatilityInterpolatorProperties(properties.copy() .with(PROPERTY_SPLINE_INTERPOLATOR, interpolatorName) .with(PROPERTY_SPLINE_LEFT_EXTRAPOLATOR, leftExtrapolatorName) .with(PROPERTY_SPLINE_RIGHT_EXTRAPOLATOR, rightExtrapolatorName) .with(PROPERTY_SPLINE_EXTRAPOLATOR_FAILURE, extrapolatorFailureBehaviour) .get(), desiredValue, SPLINE); } public static Set<ValueRequirement> ensureSplineVolatilityInterpolatorProperties(final ValueProperties constraints) { final Set<ValueRequirement> requirements = ensureCommonVolatilityInterpolatorProperties(constraints); if (requirements == null) { return null; } final Set<String> interpolatorNames = constraints.getValues(PROPERTY_SPLINE_INTERPOLATOR); if (interpolatorNames == null || interpolatorNames.size() != 1) { return null; } final Set<String> leftExtrapolatorNames = constraints.getValues(PROPERTY_SPLINE_LEFT_EXTRAPOLATOR); if (leftExtrapolatorNames == null || leftExtrapolatorNames.size() != 1) { return null; } final Set<String> rightExtrapolatorNames = constraints.getValues(PROPERTY_SPLINE_RIGHT_EXTRAPOLATOR); if (rightExtrapolatorNames == null || rightExtrapolatorNames.size() != 1) { return null; } final Set<String> extrapolatorFailureNames = constraints.getValues(PROPERTY_SPLINE_EXTRAPOLATOR_FAILURE); if (extrapolatorFailureNames == null || extrapolatorFailureNames.size() != 1) { return null; } return Collections.emptySet(); } public static ValueProperties.Builder addMixedLogNormalVolatilityInterpolatorProperties(final ValueProperties properties) { return addCommonVolatilityInterpolatorProperties(properties.copy() .withAny(PROPERTY_MIXED_LOG_NORMAL_WEIGHTING_FUNCTION).get(), MIXED_LOG_NORMAL); } public static ValueProperties.Builder addMixedLogNormalVolatilityInterpolatorProperties(final ValueProperties properties, final ValueRequirement desiredValue) { final String weightingFunction = desiredValue.getConstraint(PROPERTY_MIXED_LOG_NORMAL_WEIGHTING_FUNCTION); return addCommonVolatilityInterpolatorProperties(properties.copy() .with(PROPERTY_MIXED_LOG_NORMAL_WEIGHTING_FUNCTION, weightingFunction).get(), desiredValue, MIXED_LOG_NORMAL); } public static Set<ValueRequirement> ensureMixedLogNormalVolatilityInterpolatorProperties(final ValueProperties constraints) { final Set<ValueRequirement> requirements = ensureCommonVolatilityInterpolatorProperties(constraints); if (requirements == null) { return null; } final Set<String> weightingFunctionNames = constraints.getValues(PROPERTY_MIXED_LOG_NORMAL_WEIGHTING_FUNCTION); if (weightingFunctionNames == null || weightingFunctionNames.size() != 1) { return null; } return requirements; } public static ValueProperties.Builder addSABRVolatilityInterpolatorProperties(final ValueProperties properties) { return addCommonVolatilityInterpolatorProperties(properties.copy() .withAny(PROPERTY_SABR_MODEL) .withAny(PROPERTY_SABR_WEIGHTING_FUNCTION) .withAny(PROPERTY_SABR_USE_EXTERNAL_BETA) .withAny(PROPERTY_SABR_EXTERNAL_BETA).get(), SABR); // TODO optional? } public static ValueProperties.Builder addSABRVolatilityInterpolatorProperties(final ValueProperties properties, final ValueRequirement desiredValue) { final String model = desiredValue.getConstraint(PROPERTY_SABR_MODEL); final String weightingFunction = desiredValue.getConstraint(PROPERTY_SABR_WEIGHTING_FUNCTION); final String useExternalBeta = desiredValue.getConstraint(PROPERTY_SABR_USE_EXTERNAL_BETA); final String externalBeta = desiredValue.getConstraint(PROPERTY_SABR_EXTERNAL_BETA); return addCommonVolatilityInterpolatorProperties(properties.copy() .with(PROPERTY_SABR_MODEL, model) .with(PROPERTY_SABR_WEIGHTING_FUNCTION, weightingFunction) .with(PROPERTY_SABR_USE_EXTERNAL_BETA, useExternalBeta) .with(PROPERTY_SABR_EXTERNAL_BETA, externalBeta).get(), desiredValue, SABR); // TODO optional? } public static Set<ValueRequirement> ensureSABRVolatilityInterpolatorProperties(final ValueProperties constraints) { final Set<ValueRequirement> requirements = ensureCommonVolatilityInterpolatorProperties(constraints); if (requirements == null) { return null; } final Set<String> modelNames = constraints.getValues(PROPERTY_SABR_MODEL); if (modelNames == null || modelNames.size() != 1) { return null; } final Set<String> weightingFunctionNames = constraints.getValues(PROPERTY_SABR_WEIGHTING_FUNCTION); if (weightingFunctionNames == null || weightingFunctionNames.size() != 1) { return null; } final Set<String> useExternalBetaNames = constraints.getValues(PROPERTY_SABR_USE_EXTERNAL_BETA); if (useExternalBetaNames == null || useExternalBetaNames.size() != 1) { return null; } final Set<String> externalBetaNames = constraints.getValues(PROPERTY_SABR_EXTERNAL_BETA); if (externalBetaNames == null || externalBetaNames.size() != 1) { return null; } return requirements; } public static ValueProperties.Builder addVolatilityInterpolatorProperties(final ValueProperties properties, final ValueRequirement desiredValue) { final String smileInterpolator = desiredValue.getConstraint(PROPERTY_SMILE_INTERPOLATOR); if (SPLINE.equals(smileInterpolator)) { return BlackVolatilitySurfacePropertyUtils.addSplineVolatilityInterpolatorProperties(properties, desiredValue); } if (SABR.equals(smileInterpolator)) { return BlackVolatilitySurfacePropertyUtils.addSABRVolatilityInterpolatorProperties(properties, desiredValue); } if (MIXED_LOG_NORMAL.equals(smileInterpolator)) { return BlackVolatilitySurfacePropertyUtils.addMixedLogNormalVolatilityInterpolatorProperties(properties, desiredValue); } throw new OpenGammaRuntimeException("Did not recognise smile interpolator type " + smileInterpolator); } public static ValueProperties.Builder addVolatilityInterpolatorProperties(final ValueProperties properties, final String smileInterpolator) { if (SPLINE.equals(smileInterpolator)) { return BlackVolatilitySurfacePropertyUtils.addSplineVolatilityInterpolatorProperties(properties); } if (SABR.equals(smileInterpolator)) { return BlackVolatilitySurfacePropertyUtils.addSABRVolatilityInterpolatorProperties(properties); } if (MIXED_LOG_NORMAL.equals(smileInterpolator)) { return BlackVolatilitySurfacePropertyUtils.addMixedLogNormalVolatilityInterpolatorProperties(properties); } throw new OpenGammaRuntimeException("Did not recognise smile interpolator type " + smileInterpolator); } public static ValueProperties.Builder addBlackSurfaceProperties(final ValueProperties properties, final String instrumentType) { return properties.copy() .withAny(SURFACE) .withAny(ForwardCurveValuePropertyNames.PROPERTY_FORWARD_CURVE_CALCULATION_METHOD) .withAny(CURVE) .with(InstrumentTypeProperties.PROPERTY_SURFACE_INSTRUMENT_TYPE, instrumentType); } public static ValueProperties.Builder addBlackSurfaceProperties(final ValueProperties properties, final String instrumentType, final ValueRequirement desiredValue) { final String surface = desiredValue.getConstraint(SURFACE); final String forwardCurveCalculationMethod = desiredValue.getConstraint(ForwardCurveValuePropertyNames.PROPERTY_FORWARD_CURVE_CALCULATION_METHOD); final String curve = desiredValue.getConstraint(CURVE); return properties.copy() .with(SURFACE, surface) .with(ForwardCurveValuePropertyNames.PROPERTY_FORWARD_CURVE_CALCULATION_METHOD, forwardCurveCalculationMethod) .with(CURVE, curve) .with(InstrumentTypeProperties.PROPERTY_SURFACE_INSTRUMENT_TYPE, instrumentType); } public static ValueProperties.Builder addAllBlackSurfaceProperties(final ValueProperties properties, final String instrumentType, final String smileInterpolator) { return addBlackSurfaceProperties(addVolatilityInterpolatorProperties(properties, smileInterpolator).get(), instrumentType); } public static ValueProperties.Builder addAllBlackSurfaceProperties(final ValueProperties properties, final String instrumentType, final ValueRequirement desiredValue) { return addBlackSurfaceProperties(addVolatilityInterpolatorProperties(properties, desiredValue).get(), instrumentType, desiredValue); } public static Set<ValueRequirement> ensureAllBlackSurfaceProperties(final ValueProperties constraints) { Set<ValueRequirement> requirements = ensureCommonVolatilityInterpolatorProperties(constraints); if (requirements == null) { return null; } final Set<String> forwardCurveNames = constraints.getValues(CURVE); if (forwardCurveNames == null || forwardCurveNames.size() != 1) { return null; } final Set<String> forwardCurveCalculationMethods = constraints.getValues(ForwardCurveValuePropertyNames.PROPERTY_FORWARD_CURVE_CALCULATION_METHOD); if (forwardCurveCalculationMethods == null || forwardCurveCalculationMethods.size() != 1) { return null; } final Set<String> surfaceNames = constraints.getValues(SURFACE); if (surfaceNames == null || surfaceNames.size() != 1) { return null; } final Set<String> smileInterpolationMethods = constraints.getValues(PROPERTY_SMILE_INTERPOLATOR); if (smileInterpolationMethods == null || smileInterpolationMethods.size() != 1) { return null; } final String smileInterpolationMethod = Iterables.getOnlyElement(smileInterpolationMethods); if (smileInterpolationMethod.equals(SPLINE)) { requirements = ensureSplineVolatilityInterpolatorProperties(constraints); if (requirements == null) { return null; } } else if (smileInterpolationMethod.equals(SABR)) { requirements = ensureSABRVolatilityInterpolatorProperties(constraints); if (requirements == null) { return null; } } else if (smileInterpolationMethod.equals(MIXED_LOG_NORMAL)) { requirements = ensureMixedLogNormalVolatilityInterpolatorProperties(constraints); if (requirements == null) { return null; } } else { throw new OpenGammaRuntimeException("Did not recognise smile interpolation type " + smileInterpolationMethod); } return requirements; } public static ValueRequirement getSurfaceRequirement(final ValueRequirement desiredValue, final ValueProperties additionalConstraints, final String surfaceName, final String forwardCurveName, final String instrumentType, final ComputationTargetType targetType, final ExternalId surfaceId) { final ValueProperties constraints = desiredValue.getConstraints(); final String calculationMethod = constraints.getStrictValue(ValuePropertyNames.SURFACE_CALCULATION_METHOD); if (calculationMethod != null) { if (!BlackVolatilitySurfacePropertyNamesAndValues.INTERPOLATED_BLACK_LOGNORMAL.equals(calculationMethod)) { return null; } } final String interpolationMethod = desiredValue.getConstraint(PROPERTY_SMILE_INTERPOLATOR); if (interpolationMethod == null) { s_logger.error("No value set for interpolation method"); return null; } ValueProperties interpolationProperties = addBlackSurfaceProperties(additionalConstraints, instrumentType).get(); if (interpolationMethod.equals(SABR)) { interpolationProperties = addSABRVolatilityInterpolatorProperties(interpolationProperties).get(); } else if (interpolationMethod.equals(SPLINE)) { interpolationProperties = addSplineVolatilityInterpolatorProperties(interpolationProperties).get(); } else if (interpolationMethod.equals(MIXED_LOG_NORMAL)) { interpolationProperties = addMixedLogNormalVolatilityInterpolatorProperties(interpolationProperties).get(); } else { s_logger.error("Could not handle interpolation method {}", interpolationMethod); return null; } final ValueProperties.Builder allProperties = interpolationProperties.copy() .with(ValuePropertyNames.SURFACE, surfaceName) .with(InstrumentTypeProperties.PROPERTY_SURFACE_INSTRUMENT_TYPE, instrumentType); final Set<String> interpolationPropertyNames = interpolationProperties.getProperties(); for (final String constraint : constraints.getProperties()) { if (!constraints.getValues(constraint).isEmpty() && interpolationPropertyNames.contains(constraint)) { allProperties .withoutAny(constraint) .with(constraint, constraints.getValues(constraint)); } } if (constraints.getValues(ForwardCurveValuePropertyNames.PROPERTY_FORWARD_CURVE_NAME) != null) { allProperties .withoutAny(ValuePropertyNames.CURVE) .with(ValuePropertyNames.CURVE, forwardCurveName) .get(); } return new ValueRequirement(ValueRequirementNames.BLACK_VOLATILITY_SURFACE, targetType, surfaceId, allProperties.get()); } }