com.opengamma.strata.market.sensitivity.PointSensitivities.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.strata.market.sensitivity.PointSensitivities.java

Source

/**
 * Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.strata.market.sensitivity;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.DoubleUnaryOperator;
import java.util.stream.Collectors;

import org.joda.beans.Bean;
import org.joda.beans.BeanBuilder;
import org.joda.beans.BeanDefinition;
import org.joda.beans.ImmutableBean;
import org.joda.beans.JodaBeanUtils;
import org.joda.beans.MetaProperty;
import org.joda.beans.Property;
import org.joda.beans.PropertyDefinition;
import org.joda.beans.impl.direct.DirectFieldsBeanBuilder;
import org.joda.beans.impl.direct.DirectMetaBean;
import org.joda.beans.impl.direct.DirectMetaProperty;
import org.joda.beans.impl.direct.DirectMetaPropertyMap;

import com.google.common.collect.ImmutableList;
import com.opengamma.strata.basics.currency.Currency;
import com.opengamma.strata.basics.currency.FxConvertible;
import com.opengamma.strata.basics.currency.FxRateProvider;
import com.opengamma.strata.collect.Guavate;

/**
 * A collection of point sensitivities.
 * <p>
 * Contains a list of {@linkplain PointSensitivity point sensitivity} objects,
 * each referring to a specific point on a curve that was queried.
 * The order of the list has no specific meaning, but does allow duplicates.
 * <p>
 * For example, the point sensitivity for present value on a FRA might contain
 * two entries, one for the Ibor forward curve and one for the discount curve.
 * Each entry identifies the date that the curve was queried and the resulting multiplier.
 * <p>
 * When creating an instance, consider using {@link MutablePointSensitivities}.
 * <p>
 * One way of viewing this class is as a {@code Map} from a specific sensitivity
 * key to a {@code double} sensitivity value. However, instead or being structured
 * as a {@code Map}, the data is structured as a {@code List}, with the key and
 * value in each entry.
 */
@BeanDefinition(builderScope = "private")
public final class PointSensitivities implements FxConvertible<PointSensitivities>, ImmutableBean, Serializable {

    /**
     * An empty instance.
     */
    private static final PointSensitivities EMPTY = new PointSensitivities(ImmutableList.of());

    /**
     * The point sensitivities.
     * <p>
     * Each entry includes details of the market data query it relates to.
     */
    @PropertyDefinition(validate = "notNull")
    private final ImmutableList<PointSensitivity> sensitivities;

    //-------------------------------------------------------------------------
    /**
     * An empty sensitivity instance.
     * 
     * @return the empty instance
     */
    public static PointSensitivities empty() {
        return EMPTY;
    }

    /**
     * Obtains an instance from an array of sensitivity entries.
     * 
     * @param sensitivity  the sensitivity entry
     * @return the sensitivities instance
     */
    public static PointSensitivities of(PointSensitivity... sensitivity) {
        return PointSensitivities.of(ImmutableList.copyOf(sensitivity));
    }

    /**
     * Obtains an instance from a list of sensitivity entries.
     * 
     * @param sensitivities  the list of sensitivity entries
     * @return the sensitivities instance
     */
    @SuppressWarnings("unchecked")
    public static PointSensitivities of(List<? extends PointSensitivity> sensitivities) {
        return new PointSensitivities((List<PointSensitivity>) sensitivities);
    }

    //-----------------------------------------------------------------------
    /**
     * Gets the number of sensitivity entries.
     * 
     * @return the size of the internal list of point sensitivities
     */
    public int size() {
        return sensitivities.size();
    }

    //-------------------------------------------------------------------------
    /**
     * Combines this point sensitivities with another instance.
     * <p>
     * This returns a new sensitivity instance with a combined list of point sensitivities.
     * This instance is immutable and unaffected by this method.
     * The result may contain duplicate point sensitivities.
     * 
     * @param other  the other point sensitivities
     * @return a {@code PointSensitivities} based on this one, with the other instance added
     */
    public PointSensitivities combinedWith(PointSensitivities other) {
        return new PointSensitivities(ImmutableList.<PointSensitivity>builder().addAll(sensitivities)
                .addAll(other.sensitivities).build());
    }

    //-------------------------------------------------------------------------
    /**
     * Multiplies the sensitivities in this instance by the specified factor.
     * <p>
     * The result will consist of the same entries, but with each sensitivity value multiplied.
     * This instance is immutable and unaffected by this method.
     * 
     * @param factor  the multiplicative factor
     * @return a {@code PointSensitivities} based on this one, with each sensitivity multiplied by the factor
     */
    public PointSensitivities multipliedBy(double factor) {
        return mapSensitivities(s -> s * factor);
    }

    /**
     * Applies an operation to the sensitivities in this instance.
     * <p>
     * The result will consist of the same entries, but with the operator applied to each sensitivity value.
     * This instance is immutable and unaffected by this method.
     * <p>
     * This is used to apply a mathematical operation to the sensitivity values.
     * For example, the operator could multiply the sensitivities by a constant, or take the inverse.
     * <pre>
     *   inverse = base.mapSensitivities(value -> 1 / value);
     * </pre>
     *
     * @param operator  the operator to be applied to the sensitivities
     * @return a {@code PointSensitivities} based on this one, with the operator applied to the sensitivity values
     */
    public PointSensitivities mapSensitivities(DoubleUnaryOperator operator) {
        return sensitivities.stream().map(s -> s.withSensitivity(operator.applyAsDouble(s.getSensitivity())))
                .collect(Collectors.collectingAndThen(Guavate.toImmutableList(), PointSensitivities::new));
    }

    /**
     * Normalizes the point sensitivities by sorting and merging.
     * <p>
     * The list of sensitivities is sorted and then merged.
     * Any two entries that represent the same curve query are merged.
     * For example, if there are two point sensitivities that were created based on the same curve,
     * currency and fixing date, then the entries are combined, summing the sensitivity value.
     * <p>
     * The intention is that normalization occurs after gathering all the point sensitivities.
     * <p>
     * This instance is immutable and unaffected by this method.
     * 
     * @return a {@code PointSensitivities} based on this one, with the sensitivities normalized
     */
    public PointSensitivities normalized() {
        if (sensitivities.isEmpty()) {
            return this;
        }
        List<PointSensitivity> mutable = new ArrayList<>();
        for (PointSensitivity sensi : sensitivities) {
            insert(mutable, sensi);
        }
        return new PointSensitivities(mutable);
    }

    //-----------------------------------------------------------------------
    /**
     * Returns a mutable version of this object.
     * <p>
     * The result is an instance of the mutable {@link MutablePointSensitivities}.
     * It will contain the same individual sensitivity entries.
     * 
     * @return the mutable sensitivity instance, not null
     */
    public MutablePointSensitivities toMutable() {
        return new MutablePointSensitivities(sensitivities);
    }

    //-------------------------------------------------------------------------
    /**
     * Checks if this sensitivity equals another within the specified tolerance.
     * <p>
     * This returns true if the two instances have the list of {@code PointSensitivity},
     * where the sensitivity {@code double} values are compared within the specified tolerance.
     * It is expected that this comparator will be used on the normalized version of the sensitivity.
     * 
     * @param other  the other sensitivity
     * @param tolerance  the tolerance
     * @return true if equal up to the tolerance
     */
    public boolean equalWithTolerance(PointSensitivities other, double tolerance) {
        ImmutableList<PointSensitivity> list1 = this.getSensitivities();
        ImmutableList<PointSensitivity> list2 = other.getSensitivities();
        int nbList1 = list1.size();
        int nbList2 = list2.size();
        if (nbList1 != nbList2) {
            return false;
        }
        for (int i1 = 0; i1 < nbList1; i1++) {
            if (list1.get(i1).compareKey(list2.get(i1)) == 0) {
                if (Math.abs(list1.get(i1).getSensitivity() - list2.get(i1).getSensitivity()) > tolerance) {
                    return false;
                }
            } else {
                return false;
            }
        }
        return true;
    }

    //-------------------------------------------------------------------------
    @Override
    public PointSensitivities convertedTo(Currency resultCurrency, FxRateProvider rateProvider) {
        List<PointSensitivity> mutable = new ArrayList<>();
        for (PointSensitivity sensi : sensitivities) {
            insert(mutable, sensi.convertedTo(resultCurrency, rateProvider));
        }
        return new PointSensitivities(mutable);
    }

    // inserts a sensitivity into the mutable list in the right location
    // merges the entry with an existing entry if the key matches
    private static void insert(List<PointSensitivity> mutable, PointSensitivity addition) {
        int index = Collections.binarySearch(mutable, addition, PointSensitivity::compareKey);
        if (index >= 0) {
            PointSensitivity base = mutable.get(index);
            double combined = base.getSensitivity() + addition.getSensitivity();
            mutable.set(index, base.withSensitivity(combined));
        } else {
            int insertionPoint = -(index + 1);
            mutable.add(insertionPoint, addition);
        }
    }

    //------------------------- AUTOGENERATED START -------------------------
    ///CLOVER:OFF
    /**
     * The meta-bean for {@code PointSensitivities}.
     * @return the meta-bean, not null
     */
    public static PointSensitivities.Meta meta() {
        return PointSensitivities.Meta.INSTANCE;
    }

    static {
        JodaBeanUtils.registerMetaBean(PointSensitivities.Meta.INSTANCE);
    }

    /**
     * The serialization version id.
     */
    private static final long serialVersionUID = 1L;

    private PointSensitivities(List<PointSensitivity> sensitivities) {
        JodaBeanUtils.notNull(sensitivities, "sensitivities");
        this.sensitivities = ImmutableList.copyOf(sensitivities);
    }

    @Override
    public PointSensitivities.Meta metaBean() {
        return PointSensitivities.Meta.INSTANCE;
    }

    @Override
    public <R> Property<R> property(String propertyName) {
        return metaBean().<R>metaProperty(propertyName).createProperty(this);
    }

    @Override
    public Set<String> propertyNames() {
        return metaBean().metaPropertyMap().keySet();
    }

    //-----------------------------------------------------------------------
    /**
     * Gets the point sensitivities.
     * <p>
     * Each entry includes details of the market data query it relates to.
     * @return the value of the property, not null
     */
    public ImmutableList<PointSensitivity> getSensitivities() {
        return sensitivities;
    }

    //-----------------------------------------------------------------------
    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj != null && obj.getClass() == this.getClass()) {
            PointSensitivities other = (PointSensitivities) obj;
            return JodaBeanUtils.equal(sensitivities, other.sensitivities);
        }
        return false;
    }

    @Override
    public int hashCode() {
        int hash = getClass().hashCode();
        hash = hash * 31 + JodaBeanUtils.hashCode(sensitivities);
        return hash;
    }

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder(64);
        buf.append("PointSensitivities{");
        buf.append("sensitivities").append('=').append(JodaBeanUtils.toString(sensitivities));
        buf.append('}');
        return buf.toString();
    }

    //-----------------------------------------------------------------------
    /**
     * The meta-bean for {@code PointSensitivities}.
     */
    public static final class Meta extends DirectMetaBean {
        /**
         * The singleton instance of the meta-bean.
         */
        static final Meta INSTANCE = new Meta();

        /**
         * The meta-property for the {@code sensitivities} property.
         */
        @SuppressWarnings({ "unchecked", "rawtypes" })
        private final MetaProperty<ImmutableList<PointSensitivity>> sensitivities = DirectMetaProperty
                .ofImmutable(this, "sensitivities", PointSensitivities.class, (Class) ImmutableList.class);
        /**
         * The meta-properties.
         */
        private final Map<String, MetaProperty<?>> metaPropertyMap$ = new DirectMetaPropertyMap(this, null,
                "sensitivities");

        /**
         * Restricted constructor.
         */
        private Meta() {
        }

        @Override
        protected MetaProperty<?> metaPropertyGet(String propertyName) {
            switch (propertyName.hashCode()) {
            case 1226228605: // sensitivities
                return sensitivities;
            }
            return super.metaPropertyGet(propertyName);
        }

        @Override
        public BeanBuilder<? extends PointSensitivities> builder() {
            return new PointSensitivities.Builder();
        }

        @Override
        public Class<? extends PointSensitivities> beanType() {
            return PointSensitivities.class;
        }

        @Override
        public Map<String, MetaProperty<?>> metaPropertyMap() {
            return metaPropertyMap$;
        }

        //-----------------------------------------------------------------------
        /**
         * The meta-property for the {@code sensitivities} property.
         * @return the meta-property, not null
         */
        public MetaProperty<ImmutableList<PointSensitivity>> sensitivities() {
            return sensitivities;
        }

        //-----------------------------------------------------------------------
        @Override
        protected Object propertyGet(Bean bean, String propertyName, boolean quiet) {
            switch (propertyName.hashCode()) {
            case 1226228605: // sensitivities
                return ((PointSensitivities) bean).getSensitivities();
            }
            return super.propertyGet(bean, propertyName, quiet);
        }

        @Override
        protected void propertySet(Bean bean, String propertyName, Object newValue, boolean quiet) {
            metaProperty(propertyName);
            if (quiet) {
                return;
            }
            throw new UnsupportedOperationException("Property cannot be written: " + propertyName);
        }

    }

    //-----------------------------------------------------------------------
    /**
     * The bean-builder for {@code PointSensitivities}.
     */
    private static final class Builder extends DirectFieldsBeanBuilder<PointSensitivities> {

        private List<PointSensitivity> sensitivities = ImmutableList.of();

        /**
         * Restricted constructor.
         */
        private Builder() {
        }

        //-----------------------------------------------------------------------
        @Override
        public Object get(String propertyName) {
            switch (propertyName.hashCode()) {
            case 1226228605: // sensitivities
                return sensitivities;
            default:
                throw new NoSuchElementException("Unknown property: " + propertyName);
            }
        }

        @SuppressWarnings("unchecked")
        @Override
        public Builder set(String propertyName, Object newValue) {
            switch (propertyName.hashCode()) {
            case 1226228605: // sensitivities
                this.sensitivities = (List<PointSensitivity>) newValue;
                break;
            default:
                throw new NoSuchElementException("Unknown property: " + propertyName);
            }
            return this;
        }

        @Override
        public Builder set(MetaProperty<?> property, Object value) {
            super.set(property, value);
            return this;
        }

        @Override
        public Builder setString(String propertyName, String value) {
            setString(meta().metaProperty(propertyName), value);
            return this;
        }

        @Override
        public Builder setString(MetaProperty<?> property, String value) {
            super.setString(property, value);
            return this;
        }

        @Override
        public Builder setAll(Map<String, ? extends Object> propertyValueMap) {
            super.setAll(propertyValueMap);
            return this;
        }

        @Override
        public PointSensitivities build() {
            return new PointSensitivities(sensitivities);
        }

        //-----------------------------------------------------------------------
        @Override
        public String toString() {
            StringBuilder buf = new StringBuilder(64);
            buf.append("PointSensitivities.Builder{");
            buf.append("sensitivities").append('=').append(JodaBeanUtils.toString(sensitivities));
            buf.append('}');
            return buf.toString();
        }

    }

    ///CLOVER:ON
    //-------------------------- AUTOGENERATED END --------------------------
}