/** * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.instrument; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.threeten.bp.LocalDate; import com.opengamma.analytics.financial.instrument.annuity.AnnuityDefinition; import com.opengamma.analytics.financial.instrument.cash.DepositIborDefinition; import com.opengamma.analytics.financial.instrument.fra.ForwardRateAgreementDefinition; import com.opengamma.analytics.financial.instrument.payment.CouponIborDefinition; import com.opengamma.analytics.financial.instrument.payment.CouponIborGearingDefinition; import com.opengamma.analytics.financial.instrument.payment.CouponIborSpreadDefinition; import com.opengamma.analytics.financial.instrument.payment.PaymentDefinition; import com.opengamma.analytics.financial.instrument.swap.SwapFixedIborDefinition; import com.opengamma.analytics.financial.instrument.swap.SwapFixedIborSpreadDefinition; import com.opengamma.analytics.financial.instrument.swap.SwapIborIborDefinition; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.money.CurrencyAmount; import com.opengamma.util.money.MultipleCurrencyAmount; /** * Returns all of the floating cash-flows of an instrument. The notionals returned are adjusted for the year fraction * (i.e. a semi-annual swap with a notional of $1MM will return notionals of ~$0.5MM) */ public final class FloatingPayCashFlowVisitor extends InstrumentDefinitionVisitorAdapter<Object, Map<LocalDate, MultipleCurrencyAmount>> { private static final FloatingPayCashFlowVisitor INSTANCE = new FloatingPayCashFlowVisitor(); public static FloatingPayCashFlowVisitor getInstance() { return INSTANCE; } /** * If the notional is positive (i.e. the amount is to be received) returns * an empty map. Otherwise, returns a map containing a single payment date and the notional amount * multiplied by the accrual period. * @param deposit The deposit instrument, not null * @return A map containing the (single) payment date and amount, or an empty map, as appropriate */ @Override public Map<LocalDate, MultipleCurrencyAmount> visitDepositIborDefinition(final DepositIborDefinition deposit) { ArgumentChecker.notNull(deposit, "deposit"); final LocalDate endDate = deposit.getEndDate().toLocalDate(); if (deposit.getNotional() > 0) { return Collections.emptyMap(); } final double amount = -deposit.getNotional() * deposit.getRate() * deposit.getAccrualFactor(); return Collections.singletonMap(endDate, MultipleCurrencyAmount.of(CurrencyAmount.of(deposit.getCurrency(), amount))); } /** * If the notional is positive (i.e. the amount is to be received) returns * an empty map. Otherwise, returns a map containing a single payment date and the notional amount * multiplied by the accrual period. * @param deposit The deposit instrument, not null * @param data Not used * @return A map containing the (single) payment date and amount, or an empty map, as appropriate */ @Override public Map<LocalDate, MultipleCurrencyAmount> visitDepositIborDefinition(final DepositIborDefinition deposit, final Object data) { return visitDepositIborDefinition(deposit); } /** * If the notional is positive (i.e. the amount is to be received), returns * an empty map. Otherwise, returns a map containing a single payment date and the notional amount * multiplied by the accrual period. * @param coupon The coupon instrument, not null * @return A map containing the (single) payment date and amount, or an empty map, as appropriate */ @Override public Map<LocalDate, MultipleCurrencyAmount> visitCouponIborDefinition(final CouponIborDefinition coupon) { ArgumentChecker.notNull(coupon, "coupon"); final LocalDate endDate = coupon.getPaymentDate().toLocalDate(); if (coupon.getNotional() > 0) { return Collections.emptyMap(); } final double amount = -coupon.getNotional() * coupon.getPaymentYearFraction(); return Collections.singletonMap(endDate, MultipleCurrencyAmount.of(CurrencyAmount.of(coupon.getCurrency(), amount))); } /** * If the notional is positive (i.e. the amount is to be received), returns * an empty map. Otherwise, returns a map containing a single payment date and the notional amount * multiplied by the accrual period. * @param coupon The coupon instrument, not null * @param data Not used * @return A map containing the (single) payment date and amount, or an empty map, as appropriate */ @Override public Map<LocalDate, MultipleCurrencyAmount> visitCouponIborDefinition(final CouponIborDefinition coupon, final Object data) { return visitCouponIborDefinition(coupon); } /** * If the notional is positive (i.e. the amount is to be received), returns * an empty map. Otherwise, returns a map containing a single payment date and the notional amount * multiplied by the accrual period. * @param coupon The coupon instrument, not null * @return A map containing the (single) payment date and amount, or an empty map, as appropriate */ @Override public Map<LocalDate, MultipleCurrencyAmount> visitCouponIborSpreadDefinition(final CouponIborSpreadDefinition coupon) { ArgumentChecker.notNull(coupon, "coupon"); final LocalDate endDate = coupon.getPaymentDate().toLocalDate(); if (coupon.getNotional() > 0) { return Collections.emptyMap(); } final double amount = -coupon.getNotional() * coupon.getFixingPeriodAccrualFactor() * (1 + coupon.getSpread()); return Collections.singletonMap(endDate, MultipleCurrencyAmount.of(CurrencyAmount.of(coupon.getCurrency(), amount))); } /** * If the notional is positive (i.e. the amount is to be received), returns * an empty map. Otherwise, returns a map containing a single payment date and the notional amount * multiplied by the accrual period. * @param coupon The coupon instrument, not null * @param data Not used * @return A map containing the (single) payment date and amount, or an empty map, as appropriate */ @Override public Map<LocalDate, MultipleCurrencyAmount> visitCouponIborSpreadDefinition(final CouponIborSpreadDefinition coupon, final Object data) { return visitCouponIborSpreadDefinition(coupon); } /** * If the notional is positive (i.e. the amount is to be received), returns * an empty map. Otherwise, returns a map containing a single payment date and the notional amount * multiplied by the accrual period. * @param coupon The coupon instrument, not null * @return A map containing the (single) payment date and amount, or an empty map, as appropriate */ @Override public Map<LocalDate, MultipleCurrencyAmount> visitCouponIborGearingDefinition(final CouponIborGearingDefinition coupon) { ArgumentChecker.notNull(coupon, "coupon"); final LocalDate endDate = coupon.getPaymentDate().toLocalDate(); if (coupon.getNotional() > 0) { return Collections.emptyMap(); } final double amount = -coupon.getNotional() * coupon.getFixingPeriodAccrualFactor() * (coupon.getFactor() + coupon.getSpread()); return Collections.singletonMap(endDate, MultipleCurrencyAmount.of(CurrencyAmount.of(coupon.getCurrency(), amount))); } /** * If the notional is positive (i.e. the amount is to be received), returns * an empty map. Otherwise, returns a map containing a single payment date and the notional amount * multiplied by the accrual period. * @param coupon The coupon instrument, not null * @param data Not used * @return A map containing the (single) payment date and amount, or an empty map, as appropriate */ @Override public Map<LocalDate, MultipleCurrencyAmount> visitCouponIborGearingDefinition(final CouponIborGearingDefinition coupon, final Object data) { return visitCouponIborGearingDefinition(coupon); } /** * If the notional is positive (i.e. the FRA is a payer), returns an empty map. Otherwise, returns * a map containing a single payment date and the notional amount multiplied by the accrual period * @param forwardRateAgreement The FRA, not null * @return A map containing the (single) payment date and amount, or an empty map, as appropriate */ @Override public Map<LocalDate, MultipleCurrencyAmount> visitForwardRateAgreementDefinition(final ForwardRateAgreementDefinition forwardRateAgreement) { ArgumentChecker.notNull(forwardRateAgreement, "FRA"); final LocalDate endDate = forwardRateAgreement.getPaymentDate().toLocalDate(); if (forwardRateAgreement.getNotional() > 0) { return Collections.emptyMap(); } final double amount = -forwardRateAgreement.getNotional() * forwardRateAgreement.getPaymentYearFraction(); return Collections.singletonMap(endDate, MultipleCurrencyAmount.of(CurrencyAmount.of(forwardRateAgreement.getCurrency(), amount))); } /** * If the notional is positive (i.e. the FRA is a payer), returns an empty map. Otherwise, returns * a map containing a single payment date and the notional amount multiplied by the accrual period * @param forwardRateAgreement The FRA, not null * @param data Not used * @return A map containing the (single) payment date and amount, or an empty map, as appropriate */ @Override public Map<LocalDate, MultipleCurrencyAmount> visitForwardRateAgreementDefinition(final ForwardRateAgreementDefinition forwardRateAgreement, final Object data) { return visitForwardRateAgreementDefinition(forwardRateAgreement); } /** * Returns a map containing all of the floating payments to be made in an annuity. If there are no floating payments to be made, * an empty map is returned * @param annuity The annuity, not null * @return A map containing the payment dates and amounts */ @Override public Map<LocalDate, MultipleCurrencyAmount> visitAnnuityDefinition(final AnnuityDefinition<? extends PaymentDefinition> annuity) { ArgumentChecker.notNull(annuity, "annuity"); return getDatesFromAnnuity(annuity); } /** * Returns a map containing all of the floating payments to be made in an annuity. If there are no floating payments to be made, * an empty map is returned * @param annuity The annuity, not null * @param data Not used * @return A map containing the payment dates and amounts */ @Override public Map<LocalDate, MultipleCurrencyAmount> visitAnnuityDefinition(final AnnuityDefinition<? extends PaymentDefinition> annuity, final Object data) { return visitAnnuityDefinition(annuity); } /** * If the swap is a payer, returns an empty map. Otherwise, returns a map containing all of the floating payments to be made * @param swap The swap, not null * @return A map containing floating payments, or an empty map, as appropriate */ @Override public Map<LocalDate, MultipleCurrencyAmount> visitSwapFixedIborDefinition(final SwapFixedIborDefinition swap) { ArgumentChecker.notNull(swap, "swap"); if (swap.getIborLeg().isPayer()) { return swap.getIborLeg().accept(this); } return Collections.emptyMap(); } /** * If the swap is a payer, returns an empty map. Otherwise, returns a map containing all of the floating payments to be made * @param swap The swap, not null * @param data Not used * @return A map containing floating payments, or an empty map, as appropriate */ @Override public Map<LocalDate, MultipleCurrencyAmount> visitSwapFixedIborDefinition(final SwapFixedIborDefinition swap, final Object data) { return visitSwapFixedIborDefinition(swap); } /** * If the swap is a payer, returns an empty map. Otherwise, returns a map containing all of the floating payments to be made * @param swap The swap, not null * @return A map containing floating payments, or an empty map, as appropriate */ @Override public Map<LocalDate, MultipleCurrencyAmount> visitSwapFixedIborSpreadDefinition(final SwapFixedIborSpreadDefinition swap) { ArgumentChecker.notNull(swap, "swap"); if (swap.getIborLeg().isPayer()) { return swap.getIborLeg().accept(this); } return Collections.emptyMap(); } /** * If the swap is a payer, returns an empty map. Otherwise, returns a map containing all of the floating payments to be made * @param swap The swap, not null * @param data Not used * @return A map containing floating payments, or an empty map, as appropriate */ @Override public Map<LocalDate, MultipleCurrencyAmount> visitSwapFixedIborSpreadDefinition(final SwapFixedIborSpreadDefinition swap, final Object data) { return visitSwapFixedIborSpreadDefinition(swap); } /** * Returns a map containing all of the floating payments in the pay leg * @param swap The swap, not null * @return A map containing floating payments */ @Override public Map<LocalDate, MultipleCurrencyAmount> visitSwapIborIborDefinition(final SwapIborIborDefinition swap) { ArgumentChecker.notNull(swap, "swap"); if (swap.getFirstLeg().isPayer()) { return swap.getFirstLeg().accept(this); } return swap.getSecondLeg().accept(this); } /** * Returns a map containing all of the floating payments in the pay leg * @param swap The swap, not null * @param data Not used * @return A map containing floating payments */ @Override public Map<LocalDate, MultipleCurrencyAmount> visitSwapIborIborDefinition(final SwapIborIborDefinition swap, final Object data) { return visitSwapIborIborDefinition(swap); } private Map<LocalDate, MultipleCurrencyAmount> getDatesFromAnnuity(final AnnuityDefinition<? extends PaymentDefinition> annuity) { final Map<LocalDate, MultipleCurrencyAmount> result = new HashMap<>(); for (final PaymentDefinition payment : annuity.getPayments()) { final Map<LocalDate, MultipleCurrencyAmount> payments = payment.accept(this); for (final Map.Entry<LocalDate, MultipleCurrencyAmount> entry : payments.entrySet()) { final int scale = entry.getValue().getCurrencyAmounts()[0].getAmount() < 0 ? -1 : 1; final MultipleCurrencyAmount mca = entry.getValue().multipliedBy(scale); final LocalDate key = entry.getKey(); if (result.containsKey(key)) { result.put(key, result.get(key).plus(mca)); } else { result.put(key, mca); } } } return result; } }