/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.instrument.payment;
import org.apache.commons.lang.ObjectUtils;
import org.threeten.bp.LocalDate;
import org.threeten.bp.ZoneOffset;
import org.threeten.bp.ZonedDateTime;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.analytics.financial.instrument.InstrumentDefinitionVisitor;
import com.opengamma.analytics.financial.instrument.InstrumentDefinitionWithData;
import com.opengamma.analytics.financial.interestrate.payments.derivative.Coupon;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponFixed;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponFixedFxReset;
import com.opengamma.analytics.financial.interestrate.payments.derivative.Payment;
import com.opengamma.analytics.util.time.TimeCalculator;
import com.opengamma.timeseries.DoubleTimeSeries;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.money.Currency;
/**
* Class describing a fixed payment coupon with FX reset.
* The currency is the currency of the payment.
* The notional is expressed in the reference currency, from which the FX reset will be computed.
* The payment is (getNotional() * FX(at FX reset date) * _rate * getPaymentYearFraction())
* For exact description of the instrument, see reference.
* <P>
* Reference: Coupon with FX Reset Notional, OpenGamma Documentation 26, September 2014.
*/
public class CouponFixedFxResetDefinition extends CouponDefinition
implements InstrumentDefinitionWithData<Payment, DoubleTimeSeries<ZonedDateTime>> {
/** The fixed rate of the fixed coupon. */
private final double _rate;
/** The reference currency. */
private final Currency _referenceCurrency;
/** The FX fixing date. The notional used for the payment is the FX rate between the reference currency (RC) and the
* payment currency (PC): 1 RC = X . PC. */
private final ZonedDateTime _fxFixingDate;
/** The spot (delivery) date for the FX transaction underlying the FX fixing. */
private final ZonedDateTime _fxDeliveryDate;
/**
* @param currency The payment currency.
* @param paymentDate Coupon payment date.
* @param accrualStartDate Start date of the accrual period.
* @param accrualEndDate End date of the accrual period.
* @param paymentAccrualFactor Accrual factor of the accrual period.
* @param notional Coupon notional in the reference currency.
* @param rate Fixed rate.
* @param referenceCurrency The reference currency for the FX reset.
* @param fxFixingDate The FX fixing or reset date. The notional used for the payment is the FX rate between the
* reference currency (RC) and the payment currency (PC): 1 RC = X . PC.
* @param fxDeliveryDate The spot or delivery date for the FX transaction underlying the FX fixing.
*/
public CouponFixedFxResetDefinition(Currency currency, ZonedDateTime paymentDate, ZonedDateTime accrualStartDate,
ZonedDateTime accrualEndDate, double paymentAccrualFactor, double notional,
double rate, Currency referenceCurrency, ZonedDateTime fxFixingDate, ZonedDateTime fxDeliveryDate) {
super(currency, paymentDate, accrualStartDate, accrualEndDate, paymentAccrualFactor, notional);
ArgumentChecker.notNull(referenceCurrency, "reference currency");
ArgumentChecker.notNull(fxFixingDate, "FX fixing date");
ArgumentChecker.notNull(fxDeliveryDate, "FX delivery date");
_rate = rate;
_referenceCurrency = referenceCurrency;
_fxFixingDate = fxFixingDate;
_fxDeliveryDate = fxDeliveryDate;
}
/**
* Returns the fixed rate.
* @return The rate.
*/
public double getRate() {
return _rate;
}
/**
* Returns the reference currency.
* @return The currency.
*/
public Currency getReferenceCurrency() {
return _referenceCurrency;
}
/**
* Returns the FX fixing date.
* @return The date.
*/
public ZonedDateTime getFxFixingDate() {
return _fxFixingDate;
}
/**
* Returns the FX delivery date.
* @return The date.
*/
public ZonedDateTime getFxDeliveryDate() {
return _fxDeliveryDate;
}
/**
* Returns the amount paid for a given FX reset rate.
* The amount is "getNotional() * fxRate * _rate * getPaymentYearFraction()".
* @param fxRate The exchange rate between the reference currency (RC) and the payment currency (PC): 1 RC = X . PC.
* @return The amount.
*/
public double paymentAmount(double fxRate) {
return getNotional() * fxRate * _rate * getPaymentYearFraction();
}
@Override
public CouponFixedFxReset toDerivative(ZonedDateTime dateTime) {
ArgumentChecker.notNull(dateTime, "date");
LocalDate conversionDate = dateTime.toLocalDate();
LocalDate fixingDate = _fxFixingDate.toLocalDate();
ArgumentChecker.isTrue(!conversionDate.isAfter(fixingDate),
"Do not have any fixing data but are asking for a derivative at {} which is after fixing date {}",
conversionDate, fixingDate);
double paymentTime = TimeCalculator.getTimeBetween(dateTime, getPaymentDate());
double fixingTime = TimeCalculator.getTimeBetween(dateTime, _fxFixingDate);
double deliveryTime = TimeCalculator.getTimeBetween(dateTime, _fxDeliveryDate);
return new CouponFixedFxReset(getCurrency(), paymentTime, getPaymentYearFraction(), getNotional(), _rate,
_referenceCurrency, fixingTime, deliveryTime);
}
/**
* {@inheritDoc}
* The data is the time series of FX fixing rates.
*/
@Override
public Coupon toDerivative(ZonedDateTime dateTime, DoubleTimeSeries<ZonedDateTime> fxFixingHts) {
ArgumentChecker.notNull(dateTime, "date");
ArgumentChecker.notNull(fxFixingHts, "time series of FX resets");
LocalDate conversionDate = dateTime.toLocalDate();
LocalDate fixingDate = _fxFixingDate.toLocalDate();
double paymentTime = TimeCalculator.getTimeBetween(dateTime, getPaymentDate());
if (conversionDate.isAfter(fixingDate)) { // Fixing should be known
ZonedDateTime rezonedFixingDate = _fxFixingDate.toLocalDate().atStartOfDay(ZoneOffset.UTC);
Double fxRate = fxFixingHts.getValue(rezonedFixingDate);
if (fxRate == null) {
throw new OpenGammaRuntimeException("Could not get fixing value for date " + _fxFixingDate);
}
double notional = getNotional() * fxRate;
return new CouponFixed(getCurrency(), paymentTime, getPaymentYearFraction(), notional, _rate);
}
if (conversionDate.equals(fixingDate)) { // On fixing date: use fixing if present
ZonedDateTime rezonedFixingDate = _fxFixingDate.toLocalDate().atStartOfDay(ZoneOffset.UTC);
Double fxRate = fxFixingHts.getValue(rezonedFixingDate);
if (fxRate != null) { // Fixed notional
double notional = getNotional() * fxRate;
return new CouponFixed(getCurrency(), paymentTime, getPaymentYearFraction(), notional, _rate);
}
} // Default: no fixing
double fixingTime = TimeCalculator.getTimeBetween(dateTime, _fxFixingDate);
double deliveryTime = TimeCalculator.getTimeBetween(dateTime, _fxDeliveryDate);
return new CouponFixedFxReset(getCurrency(), paymentTime, getPaymentYearFraction(), getNotional(), _rate,
_referenceCurrency, fixingTime, deliveryTime);
}
@Override
public <U, V> V accept(InstrumentDefinitionVisitor<U, V> visitor, U data) {
ArgumentChecker.notNull(visitor, "visitor");
return visitor.visitCouponFixedFxResetDefinition(this, data);
}
@Override
public <V> V accept(InstrumentDefinitionVisitor<?, V> visitor) {
ArgumentChecker.notNull(visitor, "visitor");
return visitor.visitCouponFixedFxResetDefinition(this);
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + _fxFixingDate.hashCode();
result = prime * result + _fxDeliveryDate.hashCode();
long temp;
temp = Double.doubleToLongBits(_rate);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + _referenceCurrency.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
CouponFixedFxResetDefinition other = (CouponFixedFxResetDefinition) obj;
if (!ObjectUtils.equals(_fxFixingDate, other._fxFixingDate)) {
return false;
}
if (!ObjectUtils.equals(_fxDeliveryDate, other._fxDeliveryDate)) {
return false;
}
if (Double.doubleToLongBits(_rate) != Double.doubleToLongBits(other._rate)) {
return false;
}
if (!ObjectUtils.equals(_referenceCurrency, other._referenceCurrency)) {
return false;
}
return true;
}
}