com.opengamma.strata.market.sensitivity.PointSensitivities Maven / Gradle / Ivy
Show all versions of strata-market Show documentation
/*
* 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.function.DoubleUnaryOperator;
import java.util.stream.Collectors;
import org.joda.beans.Bean;
import org.joda.beans.BeanBuilder;
import org.joda.beans.ImmutableBean;
import org.joda.beans.JodaBeanUtils;
import org.joda.beans.MetaBean;
import org.joda.beans.MetaProperty;
import org.joda.beans.gen.BeanDefinition;
import org.joda.beans.gen.PropertyDefinition;
import org.joda.beans.impl.direct.DirectMetaBean;
import org.joda.beans.impl.direct.DirectMetaProperty;
import org.joda.beans.impl.direct.DirectMetaPropertyMap;
import org.joda.beans.impl.direct.DirectPrivateBeanBuilder;
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.
*
* 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.
*
* 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.
*
* When creating an instance, consider using {@link MutablePointSensitivities}.
*
* 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, ImmutableBean, Serializable {
/**
* An empty instance.
*/
private static final PointSensitivities EMPTY = new PointSensitivities(ImmutableList.of());
/**
* The point sensitivities.
*
* Each entry includes details of the market data query it relates to.
*/
@PropertyDefinition(validate = "notNull")
private final ImmutableList 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) 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.
*
* 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.builder()
.addAll(sensitivities)
.addAll(other.sensitivities)
.build());
}
//-------------------------------------------------------------------------
/**
* Multiplies the sensitivities in this instance by the specified factor.
*
* 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.
*
* 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.
*
* 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.
*
* inverse = base.mapSensitivities(value -> 1 / value);
*
*
* @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.
*
* 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.
*
* The intention is that normalization occurs after gathering all the point sensitivities.
*
* 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 mutable = new ArrayList<>();
for (PointSensitivity sensi : sensitivities) {
insert(mutable, sensi);
}
return new PointSensitivities(mutable);
}
//-----------------------------------------------------------------------
/**
* Returns a mutable version of this object.
*
* 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.
*
* 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 list1 = this.getSensitivities();
ImmutableList 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 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 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 -------------------------
/**
* The meta-bean for {@code PointSensitivities}.
* @return the meta-bean, not null
*/
public static PointSensitivities.Meta meta() {
return PointSensitivities.Meta.INSTANCE;
}
static {
MetaBean.register(PointSensitivities.Meta.INSTANCE);
}
/**
* The serialization version id.
*/
private static final long serialVersionUID = 1L;
private PointSensitivities(
List sensitivities) {
JodaBeanUtils.notNull(sensitivities, "sensitivities");
this.sensitivities = ImmutableList.copyOf(sensitivities);
}
@Override
public PointSensitivities.Meta metaBean() {
return PointSensitivities.Meta.INSTANCE;
}
//-----------------------------------------------------------------------
/**
* Gets the point sensitivities.
*
* Each entry includes details of the market data query it relates to.
* @return the value of the property, not null
*/
public ImmutableList 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> sensitivities = DirectMetaProperty.ofImmutable(
this, "sensitivities", PointSensitivities.class, (Class) ImmutableList.class);
/**
* The meta-properties.
*/
private final Map> 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> metaPropertyMap() {
return metaPropertyMap$;
}
//-----------------------------------------------------------------------
/**
* The meta-property for the {@code sensitivities} property.
* @return the meta-property, not null
*/
public MetaProperty> 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 DirectPrivateBeanBuilder {
private List 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) newValue;
break;
default:
throw new NoSuchElementException("Unknown property: " + propertyName);
}
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();
}
}
//-------------------------- AUTOGENERATED END --------------------------
}