com.opengamma.strata.pricer.impl.cms.DiscountingCmsPeriodPricer Maven / Gradle / Ivy
/*
* Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.pricer.impl.cms;
import java.time.LocalDate;
import java.util.OptionalDouble;
import com.opengamma.strata.basics.currency.Currency;
import com.opengamma.strata.basics.currency.CurrencyAmount;
import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.collect.Messages;
import com.opengamma.strata.market.sensitivity.PointSensitivityBuilder;
import com.opengamma.strata.pricer.ZeroRateSensitivity;
import com.opengamma.strata.pricer.rate.RatesProvider;
import com.opengamma.strata.pricer.swap.DiscountingSwapProductPricer;
import com.opengamma.strata.product.cms.CmsPeriod;
import com.opengamma.strata.product.cms.CmsPeriodType;
import com.opengamma.strata.product.swap.ResolvedSwap;
/**
* Computes the price of a CMS coupon by simple forward estimation.
*
* This is an overly simplistic approach to CMS coupon pricer. It is provided only for testing and comparison
* purposes. It is not recommended to use this for valuation or risk management purposes.
*/
public class DiscountingCmsPeriodPricer {
/**
* Default implementation.
*/
public static final DiscountingCmsPeriodPricer DEFAULT = new DiscountingCmsPeriodPricer(
DiscountingSwapProductPricer.DEFAULT);
/**
* Pricer for the underlying swap.
*/
private final DiscountingSwapProductPricer swapPricer;
/**
* Creates an instance.
*
* @param swapPricer the pricer for {@link ResolvedSwap}
*/
public DiscountingCmsPeriodPricer(
DiscountingSwapProductPricer swapPricer) {
this.swapPricer = ArgChecker.notNull(swapPricer, "legPricer");
}
//-------------------------------------------------------------------------
/**
* Computes the present value of CMS coupon by simple forward rate estimation.
*
* @param cmsPeriod the CMS
* @param provider the rates provider
* @return the present value
*/
public CurrencyAmount presentValue(
CmsPeriod cmsPeriod,
RatesProvider provider) {
Currency ccy = cmsPeriod.getCurrency();
LocalDate valuationDate = provider.getValuationDate();
if (valuationDate.isAfter(cmsPeriod.getPaymentDate())) {
return CurrencyAmount.zero(ccy);
}
LocalDate fixingDate = cmsPeriod.getFixingDate();
double dfPayment = provider.discountFactor(ccy, cmsPeriod.getPaymentDate());
if (!fixingDate.isAfter(valuationDate)) { // Using fixing
OptionalDouble fixedRate = provider.timeSeries(cmsPeriod.getIndex()).get(fixingDate);
if (fixedRate.isPresent()) {
double payoff = 0d;
switch (cmsPeriod.getCmsPeriodType()) {
case CAPLET:
payoff = Math.max(fixedRate.getAsDouble() - cmsPeriod.getStrike(), 0d);
break;
case FLOORLET:
payoff = Math.max(cmsPeriod.getStrike() - fixedRate.getAsDouble(), 0d);
break;
case COUPON:
payoff = fixedRate.getAsDouble();
break;
default:
throw new IllegalArgumentException("unsupported CMS type");
}
return CurrencyAmount.of(ccy, payoff * dfPayment * cmsPeriod.getNotional() * cmsPeriod.getYearFraction());
} else if (fixingDate.isBefore(valuationDate)) {
throw new IllegalArgumentException(Messages.format(
"Unable to get fixing for {} on date {}, no time-series supplied", cmsPeriod.getIndex(), fixingDate));
}
}
if (!cmsPeriod.getCmsPeriodType().equals(CmsPeriodType.COUPON)) {
throw new IllegalArgumentException("Unable to price cap or floor in this pricer");
}
// Using forward
ResolvedSwap swap = cmsPeriod.getUnderlyingSwap();
double forward = swapPricer.parRate(swap, provider);
return CurrencyAmount.of(ccy, forward * dfPayment * cmsPeriod.getNotional() * cmsPeriod.getYearFraction());
}
/**
* Computes the forward rate associated to the swap underlying the CMS period.
*
* Returns a value only if the period has not fixed yet. If the fixing date is on or before the valuation date,
* an {@link IllegalArgumentException} is thrown.
*
* @param cmsPeriod the CMS
* @param provider the rates provider
* @return the forward rate
*/
public double forwardRate(
CmsPeriod cmsPeriod,
RatesProvider provider) {
LocalDate fixingDate = cmsPeriod.getFixingDate();
LocalDate valuationDate = provider.getValuationDate();
if (!fixingDate.isAfter(valuationDate)) { // Using fixing
throw new IllegalArgumentException("Forward rate is availaible only for valuation date after the fixing date");
}
ResolvedSwap swap = cmsPeriod.getUnderlyingSwap();
return swapPricer.parRate(swap, provider);
}
//-------------------------------------------------------------------------
/**
* Computes the present value curve sensitivity by simple forward rate estimation.
*
* @param cmsPeriod the CMS
* @param provider the rates provider
* @return the present value sensitivity
*/
public PointSensitivityBuilder presentValueSensitivity(
CmsPeriod cmsPeriod,
RatesProvider provider) {
Currency ccy = cmsPeriod.getCurrency();
LocalDate valuationDate = provider.getValuationDate();
if (valuationDate.isAfter(cmsPeriod.getPaymentDate())) {
return PointSensitivityBuilder.none();
}
LocalDate fixingDate = cmsPeriod.getFixingDate();
double dfPayment = provider.discountFactor(ccy, cmsPeriod.getPaymentDate());
if (!fixingDate.isAfter(valuationDate)) { // Using fixing
OptionalDouble fixedRate = provider.timeSeries(cmsPeriod.getIndex()).get(fixingDate);
if (fixedRate.isPresent()) {
double payoff = 0d;
switch (cmsPeriod.getCmsPeriodType()) {
case CAPLET:
payoff = Math.max(fixedRate.getAsDouble() - cmsPeriod.getStrike(), 0d);
break;
case FLOORLET:
payoff = Math.max(cmsPeriod.getStrike() - fixedRate.getAsDouble(), 0d);
break;
case COUPON:
payoff = fixedRate.getAsDouble();
break;
default:
throw new IllegalArgumentException("unsupported CMS type");
}
return provider.discountFactors(ccy).zeroRatePointSensitivity(
cmsPeriod.getPaymentDate()).multipliedBy(payoff * cmsPeriod.getNotional() * cmsPeriod.getYearFraction());
} else if (fixingDate.isBefore(valuationDate)) {
throw new IllegalArgumentException(Messages.format(
"Unable to get fixing for {} on date {}, no time-series supplied", cmsPeriod.getIndex(), fixingDate));
}
}
if (!cmsPeriod.getCmsPeriodType().equals(CmsPeriodType.COUPON)) {
throw new IllegalArgumentException("Unable to price cap or floor in this pricer");
}
// Using forward
ResolvedSwap swap = cmsPeriod.getUnderlyingSwap();
ZeroRateSensitivity dfPaymentdr = provider.discountFactors(ccy).zeroRatePointSensitivity(cmsPeriod.getPaymentDate());
double forward = swapPricer.parRate(swap, provider);
PointSensitivityBuilder forwardSensi = swapPricer.parRateSensitivity(swap, provider);
return forwardSensi.multipliedBy(dfPayment).combinedWith(dfPaymentdr.multipliedBy(forward))
.multipliedBy(cmsPeriod.getNotional() * cmsPeriod.getYearFraction());
}
}