/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.analytics.model.pnl; import java.util.Collection; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.analytics.financial.schedule.ScheduleCalculatorFactory; import com.opengamma.analytics.financial.schedule.TimeSeriesSamplingFunctionFactory; 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.financial.analytics.timeseries.DateConstraint; import com.opengamma.financial.analytics.timeseries.HistoricalTimeSeriesFunctionUtils; import com.opengamma.financial.analytics.timeseries.HistoricalValuationFunction; import com.opengamma.financial.security.CurrenciesVisitor; import com.opengamma.financial.security.FinancialSecurityUtils; import com.opengamma.timeseries.date.DateDoubleTimeSeries; import com.opengamma.timeseries.date.localdate.ImmutableLocalDateDoubleTimeSeries; import com.opengamma.timeseries.date.localdate.LocalDateDoubleTimeSeries; import com.opengamma.util.async.AsynchronousExecution; import com.opengamma.util.money.Currency; /** * Calculates a PnL series by performing a full historical valuation over the required period. */ public class HistoricalValuationPnLFunction extends AbstractFunction.NonCompiledInvoker { @Override public ComputationTargetType getTargetType() { return ComputationTargetType.POSITION; } @Override public Set<ValueSpecification> getResults(FunctionCompilationContext context, ComputationTarget target) { return ImmutableSet.of(new ValueSpecification(ValueRequirementNames.PNL_SERIES, target.toSpecification(), ValueProperties.all())); } @Override public Set<ValueRequirement> getRequirements(FunctionCompilationContext context, ComputationTarget target, ValueRequirement desiredValue) { final ValueProperties outputConstraints = desiredValue.getConstraints(); Set<String> startDates = desiredValue.getConstraints().getValues(HistoricalTimeSeriesFunctionUtils.START_DATE_PROPERTY); if (startDates == null || startDates.size() != 1) { return null; } Set<String> endDates = desiredValue.getConstraints().getValues(HistoricalTimeSeriesFunctionUtils.END_DATE_PROPERTY); if (endDates == null || endDates.size() != 1) { return null; } Set<String> desiredCurrencies = desiredValue.getConstraints().getValues(ValuePropertyNames.CURRENCY); if (desiredCurrencies == null || desiredCurrencies.isEmpty()) { Collection<Currency> targetCurrencies = CurrenciesVisitor.getCurrencies(target.getPosition().getSecurity(), context.getSecuritySource()); // REVIEW jonathan 2013-03-12 -- should we pass through all the currencies and see what it wants to produce? desiredCurrencies = ImmutableSet.of(Iterables.get(targetCurrencies, 0).getCode()); } String pnlSeriesStartDateProperty = ValueRequirementNames.PNL_SERIES + "_" + HistoricalTimeSeriesFunctionUtils.START_DATE_PROPERTY; ValueProperties.Builder requirementConstraints = desiredValue.getConstraints().copy() .withoutAny(ValuePropertyNames.CURRENCY) .with(HistoricalValuationFunction.PASSTHROUGH_PREFIX + ValuePropertyNames.CURRENCY, desiredCurrencies) .withoutAny(ValuePropertyNames.PROPERTY_PNL_CONTRIBUTIONS) .withoutAny(ValuePropertyNames.SCHEDULE_CALCULATOR) .withoutAny(ValuePropertyNames.SAMPLING_FUNCTION) .withoutAny(HistoricalTimeSeriesFunctionUtils.START_DATE_PROPERTY) .with(pnlSeriesStartDateProperty, outputConstraints.getValues(HistoricalTimeSeriesFunctionUtils.START_DATE_PROPERTY)) .withOptional(pnlSeriesStartDateProperty); String startDate = getPriceSeriesStart(outputConstraints); if (startDate == null) { return null; } requirementConstraints.with(HistoricalTimeSeriesFunctionUtils.START_DATE_PROPERTY, startDate); removeTransformationProperties(requirementConstraints); return ImmutableSet.of(new ValueRequirement(ValueRequirementNames.HISTORICAL_TIME_SERIES, target.toSpecification(), requirementConstraints.get())); } @Override public Set<ValueSpecification> getResults(FunctionCompilationContext context, ComputationTarget target, Map<ValueSpecification, ValueRequirement> inputs) { Entry<ValueSpecification, ValueRequirement> tsInput = Iterables.getOnlyElement(inputs.entrySet()); ValueSpecification tsSpec = tsInput.getKey(); String pnlStartDate = tsInput.getValue().getConstraint(ValueRequirementNames.PNL_SERIES + "_" + HistoricalTimeSeriesFunctionUtils.START_DATE_PROPERTY); ValueSpecification valueSpec = getResultSpec(target, tsSpec.getProperties(), pnlStartDate, null); return ImmutableSet.of(valueSpec); } @Override public Set<ComputedValue> execute(FunctionExecutionContext executionContext, FunctionInputs inputs, ComputationTarget target, Set<ValueRequirement> desiredValues) throws AsynchronousExecution { ValueRequirement desiredValue = Iterables.getOnlyElement(desiredValues); ComputedValue priceTsComputedValue = inputs.getComputedValue(ValueRequirementNames.HISTORICAL_TIME_SERIES); LocalDateDoubleTimeSeries priceTs = (LocalDateDoubleTimeSeries) priceTsComputedValue.getValue(); if (priceTs.isEmpty()) { return null; } DateDoubleTimeSeries<?> pnlTs = calculatePnlSeries(priceTs, executionContext, desiredValue); String pnlStartDate = desiredValue.getConstraint(HistoricalTimeSeriesFunctionUtils.START_DATE_PROPERTY); ValueSpecification valueSpec = getResultSpec(target, priceTsComputedValue.getSpecification().getProperties(), pnlStartDate, desiredValue); return ImmutableSet.of(new ComputedValue(valueSpec, pnlTs)); } //------------------------------------------------------------------------- protected String getPriceSeriesStart(ValueProperties outputConstraints) { Set<String> samplingPeriodValues = outputConstraints.getValues(ValuePropertyNames.SAMPLING_PERIOD); if (samplingPeriodValues != null && !samplingPeriodValues.isEmpty()) { return DateConstraint.VALUATION_TIME.minus(samplingPeriodValues.iterator().next()).toString(); } Set<String> startDates = outputConstraints.getValues(HistoricalTimeSeriesFunctionUtils.START_DATE_PROPERTY); if (startDates != null && !startDates.isEmpty()) { return Iterables.getOnlyElement(startDates); } return null; } protected void removeTransformationProperties(ValueProperties.Builder builder) { builder.withoutAny(ValuePropertyNames.TRANSFORMATION_METHOD); } protected void addTransformationProperties(ValueProperties.Builder builder, ValueRequirement desiredValue) { builder.with(ValuePropertyNames.TRANSFORMATION_METHOD, "None"); } protected DateDoubleTimeSeries<?> calculatePnlSeries(LocalDateDoubleTimeSeries priceSeries, FunctionExecutionContext executionContext, ValueRequirement desiredValue) { int pnlVectorSize = priceSeries.size() - 1; double[] pnlValues = new double[pnlVectorSize]; for (int i = 0; i < pnlVectorSize; i++) { pnlValues[i] = priceSeries.getValueAtIndex(i + 1) - priceSeries.getValueAtIndex(i); } int[] pnlDates = new int[priceSeries.size() - 1]; System.arraycopy(priceSeries.timesArrayFast(), 1, pnlDates, 0, pnlDates.length); LocalDateDoubleTimeSeries pnlTs = ImmutableLocalDateDoubleTimeSeries.of(pnlDates, pnlValues); return pnlTs; } //------------------------------------------------------------------------- private ValueSpecification getResultSpec(ComputationTarget target, ValueProperties priceTsProperties, String pnlStartDate, ValueRequirement desiredValue) { Set<String> currencies = priceTsProperties.getValues(HistoricalValuationFunction.PASSTHROUGH_PREFIX + ValuePropertyNames.CURRENCY); if (currencies == null || currencies.size() != 1) { throw new OpenGammaRuntimeException("Expected a single currency for historical valuation series but got " + currencies); } ValueProperties.Builder builder = priceTsProperties.copy() .withoutAny(ValuePropertyNames.FUNCTION) .with(ValuePropertyNames.FUNCTION, getUniqueId()) .with(ValuePropertyNames.CURRENCY, currencies) .with(ValuePropertyNames.PROPERTY_PNL_CONTRIBUTIONS, "Full") .withoutAny(HistoricalTimeSeriesFunctionUtils.START_DATE_PROPERTY) .with(HistoricalTimeSeriesFunctionUtils.START_DATE_PROPERTY, pnlStartDate) .with(ValuePropertyNames.SCHEDULE_CALCULATOR, ScheduleCalculatorFactory.DAILY) .with(ValuePropertyNames.SAMPLING_FUNCTION, TimeSeriesSamplingFunctionFactory.NO_PADDING); addTransformationProperties(builder, desiredValue); return new ValueSpecification(ValueRequirementNames.PNL_SERIES, target.toSpecification(), builder.get()); } }