/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.analytics.model.var;
import static com.opengamma.financial.analytics.model.var.NormalHistoricalVaRFunction.PROPERTY_VAR_DISTRIBUTION;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.analytics.financial.var.EmpiricalDistributionVaRParameters;
import com.opengamma.analytics.financial.var.conditional.EmpiricalDistributionConditionalVaRCalculator;
import com.opengamma.analytics.math.statistics.descriptive.StatisticsCalculatorFactory;
import com.opengamma.engine.ComputationTarget;
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.timeseries.DoubleTimeSeries;
/**
*
*/
public class EmpiricalHistoricalConditionalVaRFunction extends AbstractFunction.NonCompiledInvoker {
private static final EmpiricalDistributionConditionalVaRCalculator CALCULATOR = new EmpiricalDistributionConditionalVaRCalculator(StatisticsCalculatorFactory.MEAN_CALCULATOR);
@Override
public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) {
final String currency = getCurrency(inputs);
final Object pnlSeriesObj = inputs.getValue(ValueRequirementNames.PNL_SERIES);
if (pnlSeriesObj == null) {
throw new OpenGammaRuntimeException("Could not get P&L series for " + target);
}
final DoubleTimeSeries<?> pnlSeries = (DoubleTimeSeries<?>) pnlSeriesObj;
if (pnlSeries.isEmpty()) {
throw new OpenGammaRuntimeException("P&L series for " + target + " was empty");
}
final ValueRequirement desiredValue = desiredValues.iterator().next();
final Set<String> scheduleCalculatorNames = desiredValue.getConstraints().getValues(ValuePropertyNames.SCHEDULE_CALCULATOR);
final Set<String> confidenceLevelNames = desiredValue.getConstraints().getValues(ValuePropertyNames.CONFIDENCE_LEVEL);
final Set<String> horizonNames = desiredValue.getConstraints().getValues(ValuePropertyNames.HORIZON);
final EmpiricalDistributionVaRParameters parameters = getParameters(scheduleCalculatorNames, horizonNames, confidenceLevelNames);
final double var = CALCULATOR.evaluate(parameters, pnlSeries).getVaRValue();
final ValueProperties resultProperties = getResultProperties(currency, desiredValues.iterator().next());
return Sets.newHashSet(new ComputedValue(new ValueSpecification(ValueRequirementNames.CONDITIONAL_HISTORICAL_VAR, target.toSpecification(), resultProperties), var));
}
private String getCurrency(final FunctionInputs inputs) {
String currency = null;
for (final ComputedValue value : inputs.getAllValues()) {
currency = value.getSpecification().getProperty(ValuePropertyNames.CURRENCY);
if (currency != null) {
break;
}
}
return currency;
}
@Override
public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) {
final ValueProperties properties = createValueProperties()
.withAny(ValuePropertyNames.CURRENCY)
.withAny(ValuePropertyNames.SAMPLING_PERIOD)
.withAny(ValuePropertyNames.SCHEDULE_CALCULATOR)
.withAny(ValuePropertyNames.SAMPLING_FUNCTION)
.withAny(ValuePropertyNames.CONFIDENCE_LEVEL)
.withAny(ValuePropertyNames.HORIZON)
.withAny(ValuePropertyNames.AGGREGATION)
.with(PROPERTY_VAR_DISTRIBUTION, EmpiricalHistoricalVaRFunction.EMPIRICAL_VAR).get();
return Sets.newHashSet(new ValueSpecification(ValueRequirementNames.CONDITIONAL_HISTORICAL_VAR, target.toSpecification(), properties));
}
@Override
public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) {
final ValueProperties constraints = desiredValue.getConstraints();
final Set<String> samplingPeriodName = constraints.getValues(ValuePropertyNames.SAMPLING_PERIOD);
if (samplingPeriodName == null || samplingPeriodName.size() != 1) {
return null;
}
final Set<String> scheduleCalculatorName = constraints.getValues(ValuePropertyNames.SCHEDULE_CALCULATOR);
if (scheduleCalculatorName == null || scheduleCalculatorName.size() != 1) {
return null;
}
final Set<String> samplingFunctionName = constraints.getValues(ValuePropertyNames.SAMPLING_FUNCTION);
if (samplingFunctionName == null || samplingFunctionName.size() != 1) {
return null;
}
final Set<String> aggregationStyle = constraints.getValues(ValuePropertyNames.AGGREGATION);
final ValueProperties.Builder properties = ValueProperties.builder()
.with(ValuePropertyNames.SAMPLING_PERIOD, samplingPeriodName.iterator().next())
.with(ValuePropertyNames.SCHEDULE_CALCULATOR, scheduleCalculatorName.iterator().next())
.with(ValuePropertyNames.SAMPLING_FUNCTION, samplingFunctionName.iterator().next())
.with(ValuePropertyNames.PROPERTY_PNL_CONTRIBUTIONS, "Delta"); //TODO
final Set<String> desiredCurrencyValues = desiredValue.getConstraints().getValues(ValuePropertyNames.CURRENCY);
if (desiredCurrencyValues == null || desiredCurrencyValues.isEmpty()) {
properties.withAny(ValuePropertyNames.CURRENCY);
} else {
final String desiredCurrency = Iterables.getOnlyElement(desiredCurrencyValues);
properties.with(ValuePropertyNames.CURRENCY, desiredCurrency);
}
if (aggregationStyle != null) {
if (aggregationStyle.isEmpty()) {
properties.withOptional(ValuePropertyNames.AGGREGATION);
} else {
if (constraints.isOptional(ValuePropertyNames.AGGREGATION)) {
properties.withOptional(ValuePropertyNames.AGGREGATION);
}
properties.with(ValuePropertyNames.AGGREGATION, aggregationStyle);
}
}
return Sets.newHashSet(new ValueRequirement(ValueRequirementNames.PNL_SERIES, target.toSpecification(), properties.get()));
}
@Override
public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target, final Map<ValueSpecification, ValueRequirement> inputs) {
final ValueSpecification input = inputs.keySet().iterator().next();
final String currency = input.getProperty(ValuePropertyNames.CURRENCY);
if (currency == null) {
return null;
}
final ValueProperties properties = getResultProperties(currency, input.getProperty(ValuePropertyNames.AGGREGATION));
return Sets.newHashSet(new ValueSpecification(ValueRequirementNames.CONDITIONAL_HISTORICAL_VAR, target.toSpecification(), properties));
}
private ValueProperties getResultProperties(final String currency, final String aggregationStyle) {
final ValueProperties.Builder properties = createValueProperties()
.with(ValuePropertyNames.CURRENCY, currency)
.withAny(ValuePropertyNames.SAMPLING_PERIOD)
.withAny(ValuePropertyNames.SCHEDULE_CALCULATOR)
.withAny(ValuePropertyNames.SAMPLING_FUNCTION)
.withAny(ValuePropertyNames.CONFIDENCE_LEVEL)
.withAny(ValuePropertyNames.HORIZON)
.with(PROPERTY_VAR_DISTRIBUTION, EmpiricalHistoricalVaRFunction.EMPIRICAL_VAR);
if (aggregationStyle != null) {
properties.with(ValuePropertyNames.AGGREGATION, aggregationStyle);
}
return properties.get();
}
private ValueProperties getResultProperties(final String currency, final ValueRequirement desiredValue) {
final ValueProperties.Builder properties = createValueProperties()
.with(ValuePropertyNames.CURRENCY, currency)
.with(ValuePropertyNames.SAMPLING_PERIOD, desiredValue.getConstraint(ValuePropertyNames.SAMPLING_PERIOD))
.with(ValuePropertyNames.SCHEDULE_CALCULATOR, desiredValue.getConstraint(ValuePropertyNames.SCHEDULE_CALCULATOR))
.with(ValuePropertyNames.SAMPLING_FUNCTION, desiredValue.getConstraint(ValuePropertyNames.SAMPLING_FUNCTION))
.with(ValuePropertyNames.CONFIDENCE_LEVEL, desiredValue.getConstraint(ValuePropertyNames.CONFIDENCE_LEVEL))
.with(ValuePropertyNames.HORIZON, desiredValue.getConstraint(ValuePropertyNames.HORIZON))
.with(PROPERTY_VAR_DISTRIBUTION, EmpiricalHistoricalVaRFunction.EMPIRICAL_VAR);
final String aggregationStyle = desiredValue.getConstraint(ValuePropertyNames.AGGREGATION);
if (aggregationStyle != null) {
properties.with(ValuePropertyNames.AGGREGATION, aggregationStyle);
}
return properties.get();
}
private EmpiricalDistributionVaRParameters getParameters(final Set<String> scheduleCalculatorNames, final Set<String> horizonNames, final Set<String> confidenceLevelNames) {
if (scheduleCalculatorNames == null || scheduleCalculatorNames.isEmpty() || scheduleCalculatorNames.size() != 1) {
throw new OpenGammaRuntimeException("Missing or non-unique schedule calculator name: " + scheduleCalculatorNames);
}
if (horizonNames == null || horizonNames.isEmpty() || horizonNames.size() != 1) {
throw new OpenGammaRuntimeException("Missing or non-unique horizon name: " + horizonNames);
}
if (confidenceLevelNames == null || confidenceLevelNames.isEmpty() || confidenceLevelNames.size() != 1) {
throw new OpenGammaRuntimeException("Missing or non-unique confidence level name: " + confidenceLevelNames);
}
return new EmpiricalDistributionVaRParameters(Double.valueOf(horizonNames.iterator().next()),
VaRFunctionUtils.getBusinessDaysPerPeriod(scheduleCalculatorNames.iterator().next()), Double.valueOf(confidenceLevelNames.iterator().next()));
}
@Override
public ComputationTargetType getTargetType() {
return ComputationTargetType.PORTFOLIO_NODE.or(ComputationTargetType.POSITION);
}
}