All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.opengamma.strata.calc.runner.CalculationTask Maven / Gradle / Ivy

Go to download

Provides the ability to run calculations, manage market data and create scenarios

There is a newer version: 2.12.44
Show newest version
/*
 * Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.strata.calc.runner;

import static com.opengamma.strata.collect.Guavate.toImmutableList;
import static com.opengamma.strata.collect.Guavate.toImmutableMap;
import static com.opengamma.strata.collect.Guavate.toImmutableSet;

import java.lang.invoke.MethodHandles;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import org.joda.beans.ImmutableBean;
import org.joda.beans.JodaBeanUtils;
import org.joda.beans.MetaBean;
import org.joda.beans.TypedMetaBean;
import org.joda.beans.gen.BeanDefinition;
import org.joda.beans.gen.PropertyDefinition;
import org.joda.beans.impl.light.LightMetaBean;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.opengamma.strata.basics.CalculationTarget;
import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.basics.ReferenceDataNotFoundException;
import com.opengamma.strata.basics.currency.Currency;
import com.opengamma.strata.basics.currency.CurrencyPair;
import com.opengamma.strata.basics.currency.FxRate;
import com.opengamma.strata.calc.Measure;
import com.opengamma.strata.calc.marketdata.MarketDataRequirements;
import com.opengamma.strata.calc.marketdata.MarketDataRequirementsBuilder;
import com.opengamma.strata.collect.result.FailureReason;
import com.opengamma.strata.collect.result.Result;
import com.opengamma.strata.data.FxRateId;
import com.opengamma.strata.data.MarketDataId;
import com.opengamma.strata.data.MarketDataNotFoundException;
import com.opengamma.strata.data.ObservableId;
import com.opengamma.strata.data.ObservableSource;
import com.opengamma.strata.data.scenario.ScenarioFxRateProvider;
import com.opengamma.strata.data.scenario.ScenarioMarketData;

/**
 * A single task that will be used to perform a calculation.
 * 

* This is a single unit of execution in the calculation runner. * It consists of a {@link CalculationFunction} and the appropriate inputs, * including a single {@link CalculationTarget}. When invoked, it will * calculate a result for one or more columns in the grid of results. */ @BeanDefinition(style = "light") public final class CalculationTask implements ImmutableBean { /** * The target for which the value will be calculated. * This is typically a trade. */ @PropertyDefinition(validate = "notNull") private final CalculationTarget target; /** * The function that will calculate the value. */ @PropertyDefinition(validate = "notNull") private final CalculationFunction function; /** * The additional parameters. */ @PropertyDefinition(validate = "notNull") private final CalculationParameters parameters; /** * The cells to be calculated. */ @PropertyDefinition(validate = "notEmpty") private final List cells; //------------------------------------------------------------------------- /** * Obtains an instance that will calculate the specified cells. *

* The cells must all be for the same row index and none of the column indices must overlap. * The result will contain no calculation parameters. * * @param target the target for which the value will be calculated * @param function the function that performs the calculation * @param cells the cells to be calculated by this task * @return the task */ public static CalculationTask of( CalculationTarget target, CalculationFunction function, CalculationTaskCell... cells) { return of(target, function, CalculationParameters.empty(), ImmutableList.copyOf(cells)); } /** * Obtains an instance that will calculate the specified cells. *

* The cells must all be for the same row index and none of the column indices must overlap. * * @param target the target for which the value will be calculated * @param function the function that performs the calculation * @param parameters the additional parameters * @param cells the cells to be calculated by this task * @return the task */ public static CalculationTask of( CalculationTarget target, CalculationFunction function, CalculationParameters parameters, List cells) { @SuppressWarnings("unchecked") CalculationFunction functionCast = (CalculationFunction) function; return new CalculationTask(target, functionCast, parameters, cells); } //------------------------------------------------------------------------- /** * Gets the index of the row in the grid of results. * * @return the row index */ public int getRowIndex() { return cells.get(0).getRowIndex(); } /** * Gets the set of measures that will be calculated by this task. * * @return the measures */ public Set getMeasures() { return cells.stream().map(c -> c.getMeasure()).collect(toImmutableSet()); } //------------------------------------------------------------------------- /** * Returns requirements specifying the market data the function needs to perform its calculations. * * @param refData the reference data * @return requirements specifying the market data the function needs to perform its calculations */ @SuppressWarnings("unchecked") public MarketDataRequirements requirements(ReferenceData refData) { // determine market data requirements of the function FunctionRequirements functionRequirements = function.requirements(target, getMeasures(), parameters, refData); ObservableSource obsSource = functionRequirements.getObservableSource(); // convert function requirements to market data requirements MarketDataRequirementsBuilder requirementsBuilder = MarketDataRequirements.builder(); for (ObservableId id : functionRequirements.getTimeSeriesRequirements()) { requirementsBuilder.addTimeSeries(id.withObservableSource(obsSource)); } for (MarketDataId id : functionRequirements.getValueRequirements()) { if (id instanceof ObservableId) { requirementsBuilder.addValues(((ObservableId) id).withObservableSource(obsSource)); } else { requirementsBuilder.addValues(id); } } // add requirements for the FX rates needed to convert the output values into the reporting currency for (CalculationTaskCell cell : cells) { if (cell.getMeasure().isCurrencyConvertible() && !cell.getReportingCurrency().isNone()) { Currency reportingCurrency = cell.reportingCurrency(this, refData); List> fxRateIds = functionRequirements.getOutputCurrencies().stream() .filter(outputCurrency -> !outputCurrency.equals(reportingCurrency)) .map(outputCurrency -> CurrencyPair.of(outputCurrency, reportingCurrency)) .map(pair -> FxRateId.of(pair, obsSource)) .collect(toImmutableList()); requirementsBuilder.addValues(fxRateIds); } } return requirementsBuilder.build(); } /** * Determines the natural currency of the target. *

* This is only called for measures that are currency convertible. * * @param refData the reference data * @return the natural currency */ public Currency naturalCurrency(ReferenceData refData) { return function.naturalCurrency(target, refData); } //------------------------------------------------------------------------- /** * Executes the task, performing calculations for the target using multiple sets of market data. *

* This invokes the function with the correct set of market data. * * @param marketData the market data used in the calculation * @param refData the reference data * @return results of the calculation, one for every scenario in the market data */ @SuppressWarnings("unchecked") public CalculationResults execute(ScenarioMarketData marketData, ReferenceData refData) { // calculate the results Map> results = calculate(marketData, refData); // get a suitable FX provider ScenarioFxRateProvider fxProvider = parameters.findParameter(FxRateLookup.class) .map(lookup -> LookupScenarioFxRateProvider.of(marketData, lookup)) .orElse(ScenarioFxRateProvider.of(marketData)); // convert the results, using a normal loop for better stack traces ImmutableList.Builder resultBuilder = ImmutableList.builder(); for (CalculationTaskCell cell : cells) { resultBuilder.add(cell.createResult(this, target, results, fxProvider, refData)); } // return the result return CalculationResults.of(target, resultBuilder.build()); } // calculates the result private Map> calculate(ScenarioMarketData marketData, ReferenceData refData) { try { Set requestedMeasures = getMeasures(); Set supportedMeasures = function.supportedMeasures(); Set measures = Sets.intersection(requestedMeasures, supportedMeasures); Map> map = ImmutableMap.of(); if (!measures.isEmpty()) { map = function.calculate(target, measures, parameters, marketData, refData); } // check if result does not contain all requested measures if (!map.keySet().containsAll(requestedMeasures)) { return handleMissing(requestedMeasures, supportedMeasures, map); } return map; } catch (RuntimeException ex) { return handleFailure(ex); } } // populate the result with failures private Map> handleMissing( Set requestedMeasures, Set supportedMeasures, Map> calculatedResults) { // need to add missing measures Map> updated = new HashMap<>(calculatedResults); String fnName = function.getClass().getSimpleName(); for (Measure requestedMeasure : requestedMeasures) { if (!calculatedResults.containsKey(requestedMeasure)) { if (supportedMeasures.contains(requestedMeasure)) { String msg = function.identifier(target) .map(v -> "for ID '" + v + "'") .orElse("for target '" + target.toString() + "'"); updated.put(requestedMeasure, Result.failure( FailureReason.CALCULATION_FAILED, "Function '{}' did not return requested measure '{}' {}", fnName, requestedMeasure, msg)); } else { updated.put(requestedMeasure, Result.failure( FailureReason.UNSUPPORTED, "Measure '{}' is not supported by function '{}'", requestedMeasure, fnName)); } } } return updated; } // handle the failure, extracted to aid inlining private Map> handleFailure(RuntimeException ex) { Result failure; String fnName = function.getClass().getSimpleName(); String exMsg = ex.getMessage(); Optional id = function.identifier(target); String msg = id.map(v -> " for ID '" + v + "': " + exMsg).orElse(": " + exMsg + ": for target '" + target.toString() + "'"); if (ex instanceof MarketDataNotFoundException) { failure = Result.failure( FailureReason.MISSING_DATA, ex, "Missing market data when invoking function '{}'{}", fnName, msg); } else if (ex instanceof ReferenceDataNotFoundException) { failure = Result.failure( FailureReason.MISSING_DATA, ex, "Missing reference data when invoking function '{}'{}", fnName, msg); } else if (ex instanceof UnsupportedOperationException) { failure = Result.failure( FailureReason.UNSUPPORTED, ex, "Unsupported operation when invoking function '{}'{}", fnName, msg); } else { failure = Result.failure( FailureReason.CALCULATION_FAILED, ex, "Error when invoking function '{}'{}", fnName, msg); } return getMeasures().stream().collect(toImmutableMap(m -> m, m -> failure)); } //------------------------------------------------------------------------- @Override public String toString() { return "CalculationTask" + cells; } //------------------------- AUTOGENERATED START ------------------------- /** * The meta-bean for {@code CalculationTask}. */ private static final TypedMetaBean META_BEAN = LightMetaBean.of( CalculationTask.class, MethodHandles.lookup(), new String[] { "target", "function", "parameters", "cells"}, null, null, null, ImmutableList.of()); /** * The meta-bean for {@code CalculationTask}. * @return the meta-bean, not null */ public static TypedMetaBean meta() { return META_BEAN; } static { MetaBean.register(META_BEAN); } private CalculationTask( CalculationTarget target, CalculationFunction function, CalculationParameters parameters, List cells) { JodaBeanUtils.notNull(target, "target"); JodaBeanUtils.notNull(function, "function"); JodaBeanUtils.notNull(parameters, "parameters"); JodaBeanUtils.notEmpty(cells, "cells"); this.target = target; this.function = function; this.parameters = parameters; this.cells = ImmutableList.copyOf(cells); } @Override public TypedMetaBean metaBean() { return META_BEAN; } //----------------------------------------------------------------------- /** * Gets the target for which the value will be calculated. * This is typically a trade. * @return the value of the property, not null */ public CalculationTarget getTarget() { return target; } //----------------------------------------------------------------------- /** * Gets the function that will calculate the value. * @return the value of the property, not null */ public CalculationFunction getFunction() { return function; } //----------------------------------------------------------------------- /** * Gets the additional parameters. * @return the value of the property, not null */ public CalculationParameters getParameters() { return parameters; } //----------------------------------------------------------------------- /** * Gets the cells to be calculated. * @return the value of the property, not empty */ public List getCells() { return cells; } //----------------------------------------------------------------------- @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj != null && obj.getClass() == this.getClass()) { CalculationTask other = (CalculationTask) obj; return JodaBeanUtils.equal(target, other.target) && JodaBeanUtils.equal(function, other.function) && JodaBeanUtils.equal(parameters, other.parameters) && JodaBeanUtils.equal(cells, other.cells); } return false; } @Override public int hashCode() { int hash = getClass().hashCode(); hash = hash * 31 + JodaBeanUtils.hashCode(target); hash = hash * 31 + JodaBeanUtils.hashCode(function); hash = hash * 31 + JodaBeanUtils.hashCode(parameters); hash = hash * 31 + JodaBeanUtils.hashCode(cells); return hash; } //-------------------------- AUTOGENERATED END -------------------------- }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy