/** * Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.data.scenario; import java.util.List; import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Stream; import com.opengamma.strata.collect.function.ObjIntFunction; /** * A box which can provide values for an item of market data used in scenarios. * <p> * A box can contain a single value for the data or it can contain multiple values, one for each scenario. * If it contains a single value then the same value is used in every scenario. * <p> * Wrapping the data in a box allows a simple interface for looking up market data that hides whether there * is one value or multiple values. Without the box every function that uses market data would have to * handle the two cases separately. * <p> * The box also takes care of transforming the market data when using it to build other market data values * (see the {@code apply} methods). This means that market data functions and perturbations don't need * different logic to deal with single and multiple values. * <p> * Using a box allows scenario data to be stored more efficiently in some cases. For example, curve data for * multiple scenarios can include one copy of the x-axis data which is used in all scenarios. If a separate * curve were stored for each scenario that data would be unnecessarily stored multiple times. * <p> * In some cases a function might need to access the data for all scenarios at the same time. For example, if * part of the calculation is the same for all scenarios it can be done once and reused instead of recalculated * for each scenario. In this case a {@link ScenarioMarketDataId} should be used to retrieve the scenario * value from the market data container. * * @param <T> the type of data held in the box */ public interface MarketDataBox<T> { /** * Obtains an instance containing a single market data value that is used in all scenarios. * * @param <T> the type of the market data value used in each scenario * @param singleValue the market data value containing data for a single scenario * @return a box containing a single market data value that is used in all scenarios */ public static <T> MarketDataBox<T> ofSingleValue(T singleValue) { return SingleMarketDataBox.of(singleValue); } /** * Obtains an instance containing a scenario market data value with data for multiple scenarios. * <p> * The market data is made up of multiple values, one for each scenario. * The {@link ScenarioArray} instance may provide optimized internal storage of these values. * <p> * A box may be created that contains a value for one scenario. Such a box is distinct from * a box created using {@link #ofSingleValue(Object)}, which is valid for any number of scenarios. * * @param <T> the type of the market data value used in each scenario * @param scenarioValue the market data value containing data for multiple scenarios * @return a box containing a scenario market data value with data for multiple scenarios */ public static <T> MarketDataBox<T> ofScenarioValue(ScenarioArray<T> scenarioValue) { return ScenarioMarketDataBox.of(scenarioValue); } /** * Obtains an instance containing a scenario market data value with data for multiple scenarios. * <p> * The market data is made up of multiple values, one for each scenario. * <p> * A box may be created that contains a value for one scenario. Such a box is distinct from * a box created using {@link #ofSingleValue(Object)}, which is valid for any number of scenarios. * * @param <T> the type of the market data value used in each scenario * @param scenarioValues the market data values for each scenario * @return a box containing a scenario market data value with data for multiple scenarios */ @SafeVarargs public static <T> MarketDataBox<T> ofScenarioValues(T... scenarioValues) { return ScenarioMarketDataBox.of(scenarioValues); } /** * Obtains an instance containing a scenario market data value with data for multiple scenarios. * <p> * The market data is made up of multiple values, one for each scenario. * <p> * A box may be created that contains a value for one scenario. Such a box is distinct from * a box created using {@link #ofSingleValue(Object)}, which is valid for any number of scenarios. * * @param <T> the type of the market data value used in each scenario * @param scenarioValues the market data values for each scenario * @return a box containing a scenario market data value with data for multiple scenarios */ public static <T> MarketDataBox<T> ofScenarioValues(List<T> scenarioValues) { return ScenarioMarketDataBox.of(scenarioValues); } /** * Obtains an instance containing no market data. * * @param <T> the type of the market data value used in each scenario * @return a box containing no market data */ public static <T> MarketDataBox<T> empty() { return EmptyMarketDataBox.empty(); } //------------------------------------------------------------------------- /** * Gets the single market data value used for all scenarios if available. * <p> * If this box contains data for multiple scenarios an exception is thrown. * <p> * This method should only be called if {@link #isSingleValue()} returns {@code true} * or {@link #isScenarioValue()} return {@code false}. * * @return the single market data value used for all scenarios if available * @throws UnsupportedOperationException if this box contains data for multiple scenarios */ public abstract T getSingleValue(); /** * Gets the market data value containing data for multiple scenarios. * <p> * If this box contains data for a single scenario an exception is thrown. * <p> * This method should only be called if {@link #isSingleValue()} returns {@code false} * or {@link #isScenarioValue()} return {@code true}. * * @return the market data value containing data for multiple scenarios * @throws UnsupportedOperationException if this box contains data for a single scenario */ public abstract ScenarioArray<T> getScenarioValue(); /** * Gets the market data value associated with the specified scenario. * * @param scenarioIndex the index of the scenario * @return the market data value associated with the scenario * @throws IndexOutOfBoundsException if the index is invalid */ public abstract T getValue(int scenarioIndex); //------------------------------------------------------------------------- /** * Checks if this box contains a single market data value that is used for all scenarios. * * @return true if this box contains a single market data value that is used for all scenarios */ public abstract boolean isSingleValue(); /** * Checks if this box contains market data for multiple scenarios. * * @return true if this box contains market data for multiple scenarios */ public default boolean isScenarioValue() { return !isSingleValue(); } //------------------------------------------------------------------------- /** * Gets the number of scenarios for which this box contains data. * <p> * A "single value" box can be used with any number of scenarios. * To indicate this, the method will return -1. * * @return the number of scenarios for which this box contains data, -1 if the number is not specified */ public abstract int getScenarioCount(); /** * Gets the type of the market data value used in each scenario. * * @return the type of the market data value used in each scenario */ public abstract Class<?> getMarketDataType(); //------------------------------------------------------------------------- /** * Applies a function to the contents of the box and returns another box. * <p> * The box implementation takes care of checking whether it contains a single value or a scenario value, * applying the function to the value for each scenario and packing the return value into a box. * <p> * This is primarily intended for use by market data factories which might receive single values or * scenario values from upstream market data factories. * * @param <R> the return type of the function * @param fn a function to apply to the market data in the box * @return a result wrapping a box containing the return value of the function */ public abstract <R> MarketDataBox<R> map(Function<T, R> fn); /** * Applies a function to the contents of the box once for each scenario and returns a box containing * the values returned from the function. * <p> * The {@link #getScenarioCount() scenario count} of the box must be one or it must be equal to the * {@code scenarioCount} argument. The {@link #getScenarioCount() scenario count} of the return value * will be equal to {@code scenarioCount}. * <p> * The box implementation takes care of checking whether it contains a single value or a scenario value, * applying the function to the value for each scenario and packing the return values into a box. * <p> * This is primarily intended to be used by perturbations which generate separate market data values for * each scenario data by applying a function to the existing value for the scenario. * * @param scenarioCount the total number of scenarios * @param fn the function that is invoked with a scenario index and the market data value for that scenario. * The return value is used as the scenario data in the returned box * @param <R> the type of the returned market data * @return a box containing market data created by applying the function to the contents of this box */ public abstract <R> MarketDataBox<R> mapWithIndex(int scenarioCount, ObjIntFunction<T, R> fn); /** * Applies a function to the market data in this box and another box and returns a box containing the result. * <p> * The box implementation takes care of checking whether the input boxes contain single values or a scenario values, * applying the function to the value for each scenario and packing the return value into a box. * <p> * This is primarily intended for use by market data factories which might receive single values or * scenario values from upstream market data factories. * * @param <U> the type of market data in the other box * @param <R> the type of the market data returned in the result of the function * @param other another market data box * @param fn the function invoked with the market data from each box. The return value is used to build the data * in the returned box * @return a box containing market data created by applying the function to the data in this box and another box */ public abstract <U, R> MarketDataBox<R> combineWith(MarketDataBox<U> other, BiFunction<T, U, R> fn); /** * Returns a stream over the contents of the box. * * @return a stream over the contents of the box */ public Stream<T> stream(); }