/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.financial.security.irs;
import java.util.Arrays;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import org.joda.beans.Bean;
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;
/**
* A rate, either constant or a complex schedule.
* The schedule can contain both absolute values and step changes.
*/
@BeanDefinition
public final class Rate implements ImmutableBean {
/**
* The periods for which custom spreads are required.
* If a period is looked for but is not present then
* the value from the previous period will be used.
*/
@PropertyDefinition
private final int[] _dates;
/**
* The custom rates.
*/
@PropertyDefinition
private final double[] _rates;
/**
* The adjustment types for each step. Either Delta or absolute.
*/
@PropertyDefinition
private final ShiftType[] _types;
/**
* A constant rate
*
* @param rate the rate
*/
public Rate(final double rate) {
_dates = new int[] {0};
_rates = new double[] {rate};
_types = new ShiftType[] {ShiftType.OUTRIGHT};
}
/**
* Get the initial rate.
*
* @return the initial rate
*/
public double getInitialRate() {
return getRate(0);
}
/**
* Get the rate for the given period.
*
* @param period the period
* @return the rate
*/
public double getRate(final int period) {
if (period == 0) {
return _rates[0]; // first amount must be absolute.
}
final int index = Arrays.binarySearch(_dates, period);
if (index >= 0) {
if (_types[index] == ShiftType.OUTRIGHT) {
return _rates[index]; // short circuit if we don't need to adjust from previous
}
final int previousIndex = Math.max(0, index - 1);
return _types[index].getRate(getRate(previousIndex), _rates[index]);
} else {
// if value not explicitly set for this period, take from previous.
// TODO: Calculate directly here to avoid recursive call
return getRate(-(index + 2));
}
}
/**
* Enum describing how the amount is varied by the steps within a schedule.
*/
public enum ShiftType {
/**
* Apply an adjustment to the previous amount.
* e.g. decrease by 10% each step.
*/
DELTA {
@Override
double getRate(double previous, final double step) {
return previous * step;
}
},
/**
* Apply an adjustment to the previous amount.
* e.g. decrease by 1 each step.
*/
ADDITIVE {
@Override
double getRate(double previous, final double step) {
return previous + step;
}
},
/**
* The amount given for the period is the desired value.
*/
OUTRIGHT {
@Override
double getRate(double previous, double thisRate) {
return thisRate;
}
};
abstract double getRate(final double previous, final double step);
}
//------------------------- AUTOGENERATED START -------------------------
///CLOVER:OFF
/**
* The meta-bean for {@code Rate}.
* @return the meta-bean, not null
*/
public static Rate.Meta meta() {
return Rate.Meta.INSTANCE;
}
static {
JodaBeanUtils.registerMetaBean(Rate.Meta.INSTANCE);
}
/**
* Returns a builder used to create an instance of the bean.
* @return the builder, not null
*/
public static Rate.Builder builder() {
return new Rate.Builder();
}
private Rate(
int[] dates,
double[] rates,
ShiftType[] types) {
this._dates = (dates != null ? dates.clone() : null);
this._rates = (rates != null ? rates.clone() : null);
this._types = types;
}
@Override
public Rate.Meta metaBean() {
return Rate.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 periods for which custom spreads are required.
* If a period is looked for but is not present then
* the value from the previous period will be used.
* @return the value of the property
*/
public int[] getDates() {
return (_dates != null ? _dates.clone() : null);
}
//-----------------------------------------------------------------------
/**
* Gets the custom rates.
* @return the value of the property
*/
public double[] getRates() {
return (_rates != null ? _rates.clone() : null);
}
//-----------------------------------------------------------------------
/**
* Gets the adjustment types for each step. Either Delta or absolute.
* @return the value of the property
*/
public ShiftType[] getTypes() {
return _types;
}
//-----------------------------------------------------------------------
/**
* Returns a builder that allows this bean to be mutated.
* @return the mutable builder, not null
*/
public Builder toBuilder() {
return new Builder(this);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj != null && obj.getClass() == this.getClass()) {
Rate other = (Rate) obj;
return JodaBeanUtils.equal(getDates(), other.getDates()) &&
JodaBeanUtils.equal(getRates(), other.getRates()) &&
JodaBeanUtils.equal(getTypes(), other.getTypes());
}
return false;
}
@Override
public int hashCode() {
int hash = getClass().hashCode();
hash = hash * 31 + JodaBeanUtils.hashCode(getDates());
hash = hash * 31 + JodaBeanUtils.hashCode(getRates());
hash = hash * 31 + JodaBeanUtils.hashCode(getTypes());
return hash;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(128);
buf.append("Rate{");
buf.append("dates").append('=').append(getDates()).append(',').append(' ');
buf.append("rates").append('=').append(getRates()).append(',').append(' ');
buf.append("types").append('=').append(JodaBeanUtils.toString(getTypes()));
buf.append('}');
return buf.toString();
}
//-----------------------------------------------------------------------
/**
* The meta-bean for {@code Rate}.
*/
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 dates} property.
*/
private final MetaProperty<int[]> _dates = DirectMetaProperty.ofImmutable(
this, "dates", Rate.class, int[].class);
/**
* The meta-property for the {@code rates} property.
*/
private final MetaProperty<double[]> _rates = DirectMetaProperty.ofImmutable(
this, "rates", Rate.class, double[].class);
/**
* The meta-property for the {@code types} property.
*/
private final MetaProperty<ShiftType[]> _types = DirectMetaProperty.ofImmutable(
this, "types", Rate.class, ShiftType[].class);
/**
* The meta-properties.
*/
private final Map<String, MetaProperty<?>> _metaPropertyMap$ = new DirectMetaPropertyMap(
this, null,
"dates",
"rates",
"types");
/**
* Restricted constructor.
*/
private Meta() {
}
@Override
protected MetaProperty<?> metaPropertyGet(String propertyName) {
switch (propertyName.hashCode()) {
case 95356549: // dates
return _dates;
case 108285843: // rates
return _rates;
case 110844025: // types
return _types;
}
return super.metaPropertyGet(propertyName);
}
@Override
public Rate.Builder builder() {
return new Rate.Builder();
}
@Override
public Class<? extends Rate> beanType() {
return Rate.class;
}
@Override
public Map<String, MetaProperty<?>> metaPropertyMap() {
return _metaPropertyMap$;
}
//-----------------------------------------------------------------------
/**
* The meta-property for the {@code dates} property.
* @return the meta-property, not null
*/
public MetaProperty<int[]> dates() {
return _dates;
}
/**
* The meta-property for the {@code rates} property.
* @return the meta-property, not null
*/
public MetaProperty<double[]> rates() {
return _rates;
}
/**
* The meta-property for the {@code types} property.
* @return the meta-property, not null
*/
public MetaProperty<ShiftType[]> types() {
return _types;
}
//-----------------------------------------------------------------------
@Override
protected Object propertyGet(Bean bean, String propertyName, boolean quiet) {
switch (propertyName.hashCode()) {
case 95356549: // dates
return ((Rate) bean).getDates();
case 108285843: // rates
return ((Rate) bean).getRates();
case 110844025: // types
return ((Rate) bean).getTypes();
}
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 Rate}.
*/
public static final class Builder extends DirectFieldsBeanBuilder<Rate> {
private int[] _dates;
private double[] _rates;
private ShiftType[] _types;
/**
* Restricted constructor.
*/
private Builder() {
}
/**
* Restricted copy constructor.
* @param beanToCopy the bean to copy from, not null
*/
private Builder(Rate beanToCopy) {
this._dates = (beanToCopy.getDates() != null ? beanToCopy.getDates().clone() : null);
this._rates = (beanToCopy.getRates() != null ? beanToCopy.getRates().clone() : null);
this._types = beanToCopy.getTypes();
}
//-----------------------------------------------------------------------
@Override
public Object get(String propertyName) {
switch (propertyName.hashCode()) {
case 95356549: // dates
return _dates;
case 108285843: // rates
return _rates;
case 110844025: // types
return _types;
default:
throw new NoSuchElementException("Unknown property: " + propertyName);
}
}
@Override
public Builder set(String propertyName, Object newValue) {
switch (propertyName.hashCode()) {
case 95356549: // dates
this._dates = (int[]) newValue;
break;
case 108285843: // rates
this._rates = (double[]) newValue;
break;
case 110844025: // types
this._types = (ShiftType[]) 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 Rate build() {
return new Rate(
_dates,
_rates,
_types);
}
//-----------------------------------------------------------------------
/**
* Sets the {@code dates} property in the builder.
* @param dates the new value
* @return this, for chaining, not null
*/
public Builder dates(int... dates) {
this._dates = dates;
return this;
}
/**
* Sets the {@code rates} property in the builder.
* @param rates the new value
* @return this, for chaining, not null
*/
public Builder rates(double... rates) {
this._rates = rates;
return this;
}
/**
* Sets the {@code types} property in the builder.
* @param types the new value
* @return this, for chaining, not null
*/
public Builder types(ShiftType... types) {
this._types = types;
return this;
}
//-----------------------------------------------------------------------
@Override
public String toString() {
StringBuilder buf = new StringBuilder(128);
buf.append("Rate.Builder{");
buf.append("dates").append('=').append(JodaBeanUtils.toString(_dates)).append(',').append(' ');
buf.append("rates").append('=').append(JodaBeanUtils.toString(_rates)).append(',').append(' ');
buf.append("types").append('=').append(JodaBeanUtils.toString(_types));
buf.append('}');
return buf.toString();
}
}
///CLOVER:ON
//-------------------------- AUTOGENERATED END --------------------------
}