/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.interestrate.annuity.derivative; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.commons.lang.ObjectUtils; import com.opengamma.analytics.financial.interestrate.InstrumentDerivative; import com.opengamma.analytics.financial.interestrate.InstrumentDerivativeVisitor; import com.opengamma.analytics.financial.interestrate.payments.derivative.Payment; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.money.Currency; /** * A generic annuity is a set of payments (cash flows) at known future times. All payments have the same currency. * There payments can be known in advance, or depend on the future value of some (possibly several) indices, e.g. the Libor. * @param <P> The payment type */ public class Annuity<P extends Payment> implements InstrumentDerivative { /** * The list of the annuity payments. */ private final P[] _payments; /** * Flag indicating if the annuity is payer (true) or receiver (false). Deduced from the first non-zero amount; * if all amounts don't have the same sign, the flag may be incorrect. */ private final boolean _isPayer; /** * @param payments The payments, not null or empty */ public Annuity(final P[] payments) { ArgumentChecker.noNulls(payments, "payments"); ArgumentChecker.isTrue(payments.length > 0, "Have no payments in annuity"); final Currency currency0 = payments[0].getCurrency(); double amount = payments[0].getReferenceAmount(); for (int loopcpn = 1; loopcpn < payments.length; loopcpn++) { ArgumentChecker.isTrue(currency0.equals(payments[loopcpn].getCurrency()), "currency not the same for all payments"); amount = (amount == 0) ? payments[loopcpn].getReferenceAmount() : amount; } _payments = payments; _isPayer = (amount < 0); } /** * @param payments The payments, not null or empty * @param pType The type of the payments, not null * @param isPayer True if the annuity is to be paid */ public Annuity(final List<? extends P> payments, final Class<P> pType, final boolean isPayer) { ArgumentChecker.noNulls(payments, "payments"); ArgumentChecker.notNull(pType, "type"); ArgumentChecker.isTrue(payments.size() > 0, "Payments size must be greater than zero"); _payments = payments.toArray((P[]) Array.newInstance(pType, 0)); _isPayer = isPayer; } /** * Gets the number of payments in the annuity. * @return The number of payments */ public int getNumberOfPayments() { return _payments.length; } /** * Gets the nth payment in an annuity. <b>Note that n = 0 will give the first payment</b>. * @param n The number of the payment * @return The nth payment */ public P getNthPayment(final int n) { return _payments[n]; } /** * Return the currency of the annuity. * @return The currency. */ public Currency getCurrency() { return _payments[0].getCurrency(); } /** * Check if the payments of an annuity is of the type CouponFixed or CouponIbor. Used to check that payment are of vanilla type. * @return True if IborCoupon or FixedCoupon */ public boolean isIborOrFixed() { //TODO: is this method necessary? boolean result = true; for (final P payment : _payments) { result = result && payment.isIborOrFixed(); } return result; } /** * Gets the payments array. * @return the payments */ public P[] getPayments() { return _payments; } /** * Gets the payer flag: payer (true) or receiver (false) * @return The payer flag. * @deprecated The payer flag is no longer used; the sign of the notional * determines whether a leg is paid or received */ @Deprecated public boolean isPayer() { return _isPayer; } /** * Return the discounting (or funding) curve name. Deduced from the first payment. * @return The name. * @deprecated Curve names should not be set in {@link InstrumentDerivative}s */ @Deprecated public String getDiscountCurve() { return getNthPayment(0).getFundingCurveName(); } /** * Create a new annuity with the payments of the original one paying strictly after the given time. * @param trimTime The time. * @return The trimmed annuity. */ @SuppressWarnings("unchecked") public Annuity<P> trimBefore(final double trimTime) { final List<P> list = new ArrayList<>(); list.clear(); for (final P payment : _payments) { if (payment.getPaymentTime() > trimTime) { list.add(payment); } } return new Annuity<>(list.toArray((P[]) new Payment[list.size()])); } /** * Create a new annuity with the payments of the original one paying before or on the given time. * @param trimTime The time. * @return The trimmed annuity. */ @SuppressWarnings("unchecked") public Annuity<P> trimAfter(final double trimTime) { final List<P> list = new ArrayList<>(); for (final P payment : _payments) { if (payment.getPaymentTime() <= trimTime) { list.add(payment); } } return new Annuity<>(list.toArray((P[]) new Payment[list.size()])); } @Override public String toString() { final StringBuffer result = new StringBuffer("Annuity:"); for (final P payment : _payments) { result.append(payment.toString()); result.append("\n"); } return result.toString(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + Arrays.hashCode(_payments); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Annuity<?> other = (Annuity<?>) obj; if (_payments.length != other._payments.length) { return false; } for (int i = 0; i < _payments.length; i++) { if (!ObjectUtils.equals(_payments[i], other._payments[i])) { return false; } } return true; } @Override public <S, T> T accept(final InstrumentDerivativeVisitor<S, T> visitor, final S data) { ArgumentChecker.notNull(visitor, "visitor"); return visitor.visitGenericAnnuity(this, data); } @Override public <T> T accept(final InstrumentDerivativeVisitor<?, T> visitor) { ArgumentChecker.notNull(visitor, "visitor"); return visitor.visitGenericAnnuity(this); } }