com.opengamma.strata.pricer.fra.DiscountingFraProductPricer Maven / Gradle / Ivy
/*
* Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.pricer.fra;
import static java.time.temporal.ChronoUnit.DAYS;
import java.time.LocalDate;
import com.opengamma.strata.basics.currency.Currency;
import com.opengamma.strata.basics.currency.CurrencyAmount;
import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.market.amount.CashFlow;
import com.opengamma.strata.market.amount.CashFlows;
import com.opengamma.strata.market.explain.ExplainKey;
import com.opengamma.strata.market.explain.ExplainMap;
import com.opengamma.strata.market.explain.ExplainMapBuilder;
import com.opengamma.strata.market.sensitivity.PointSensitivities;
import com.opengamma.strata.market.sensitivity.PointSensitivityBuilder;
import com.opengamma.strata.pricer.DiscountFactors;
import com.opengamma.strata.pricer.rate.RateComputationFn;
import com.opengamma.strata.pricer.rate.RatesProvider;
import com.opengamma.strata.product.fra.ResolvedFra;
import com.opengamma.strata.product.rate.RateComputation;
/**
* Pricer for for forward rate agreement (FRA) products.
*
* This provides the ability to price {@link ResolvedFra}.
* The product is priced using a forward curve for the index.
*/
public class DiscountingFraProductPricer {
/**
* Default implementation.
*/
public static final DiscountingFraProductPricer DEFAULT = new DiscountingFraProductPricer(
RateComputationFn.standard());
/**
* Rate computation.
*/
private final RateComputationFn rateComputationFn;
/**
* Creates an instance.
*
* @param rateComputationFn the rate computation function
*/
public DiscountingFraProductPricer(
RateComputationFn rateComputationFn) {
this.rateComputationFn = ArgChecker.notNull(rateComputationFn, "rateComputationFn");
}
//-------------------------------------------------------------------------
/**
* Calculates the present value of the FRA product.
*
* The present value of the product is the value on the valuation date.
* This is the discounted forecast value.
*
* @param fra the product
* @param provider the rates provider
* @return the present value of the product
*/
public CurrencyAmount presentValue(ResolvedFra fra, RatesProvider provider) {
// forecastValue * discountFactor
double df = provider.discountFactor(fra.getCurrency(), fra.getPaymentDate());
double pv = forecastValue0(fra, provider) * df;
return CurrencyAmount.of(fra.getCurrency(), pv);
}
/**
* Calculates the present value sensitivity of the FRA product.
*
* The present value sensitivity of the product is the sensitivity of the present value to
* the underlying curves.
*
* @param fra the product
* @param provider the rates provider
* @return the point sensitivity of the present value
*/
public PointSensitivities presentValueSensitivity(ResolvedFra fra, RatesProvider provider) {
if (fra.getPaymentDate().isBefore(provider.getValuationDate())) {
return PointSensitivities.empty();
}
DiscountFactors discountFactors = provider.discountFactors(fra.getCurrency());
double df = discountFactors.discountFactor(fra.getPaymentDate());
double notional = fra.getNotional();
double unitAmount = unitAmount(fra, provider);
double derivative = derivative(fra, provider);
PointSensitivityBuilder iborSens = forwardRateSensitivity(fra, provider)
.multipliedBy(derivative * df * notional);
PointSensitivityBuilder discSens = discountFactors.zeroRatePointSensitivity(fra.getPaymentDate())
.multipliedBy(unitAmount * notional);
return iborSens.withCurrency(fra.getCurrency()).combinedWith(discSens).build();
}
//-------------------------------------------------------------------------
/**
* Calculates the forecast value of the FRA product.
*
* The forecast value of the product is the value on the valuation date without present value discounting.
*
* @param fra the product
* @param provider the rates provider
* @return the forecast value of the product
*/
public CurrencyAmount forecastValue(ResolvedFra fra, RatesProvider provider) {
double fv = forecastValue0(fra, provider);
return CurrencyAmount.of(fra.getCurrency(), fv);
}
/**
* Calculates the forecast value sensitivity of the FRA product.
*
* The forecast value sensitivity of the product is the sensitivity of the forecast value to
* the underlying curves.
*
* @param fra the product
* @param provider the rates provider
* @return the point sensitivity of the forecast value
*/
public PointSensitivities forecastValueSensitivity(ResolvedFra fra, RatesProvider provider) {
if (fra.getPaymentDate().isBefore(provider.getValuationDate())) {
return PointSensitivities.empty();
}
double notional = fra.getNotional();
double derivative = derivative(fra, provider);
PointSensitivityBuilder iborSens = forwardRateSensitivity(fra, provider)
.multipliedBy(derivative * notional);
return iborSens.withCurrency(fra.getCurrency()).build();
}
//-------------------------------------------------------------------------
/**
* Calculates the par rate of the FRA product.
*
* The par rate is the rate for which the FRA present value is 0.
*
* @param fra the product
* @param provider the rates provider
* @return the par rate
*/
public double parRate(ResolvedFra fra, RatesProvider provider) {
return forwardRate(fra, provider);
}
/**
* Calculates the par rate curve sensitivity of the FRA product.
*
* The par rate curve sensitivity of the product is the sensitivity of the par rate to
* the underlying curves.
*
* @param fra the product
* @param provider the rates provider
* @return the par rate sensitivity
*/
public PointSensitivities parRateSensitivity(ResolvedFra fra, RatesProvider provider) {
return forwardRateSensitivity(fra, provider).build();
}
/**
* Calculates the par spread of the FRA product.
*
* This is spread to be added to the fixed rate to have a present value of 0.
*
* @param fra the product
* @param provider the rates provider
* @return the par spread
*/
public double parSpread(ResolvedFra fra, RatesProvider provider) {
double forward = forwardRate(fra, provider);
return forward - fra.getFixedRate();
}
/**
* Calculates the par spread curve sensitivity of the FRA product.
*
* The par spread curve sensitivity of the product is the sensitivity of the par spread to
* the underlying curves.
*
* @param fra the product
* @param provider the rates provider
* @return the par spread sensitivity
*/
public PointSensitivities parSpreadSensitivity(ResolvedFra fra, RatesProvider provider) {
return forwardRateSensitivity(fra, provider).build();
}
//-------------------------------------------------------------------------
/**
* Calculates the future cash flow of the FRA product.
*
* There is only one cash flow on the payment date for the FRA product.
* The expected currency amount of the cash flow is the same as {@link #forecastValue(ResolvedFra, RatesProvider)}.
*
* @param fra the product
* @param provider the rates provider
* @return the cash flows
*/
public CashFlows cashFlows(ResolvedFra fra, RatesProvider provider) {
LocalDate paymentDate = fra.getPaymentDate();
double forecastValue = forecastValue0(fra, provider);
double df = provider.discountFactor(fra.getCurrency(), paymentDate);
CashFlow cashFlow = CashFlow.ofForecastValue(paymentDate, fra.getCurrency(), forecastValue, df);
return CashFlows.of(cashFlow);
}
//-------------------------------------------------------------------------
/**
* Explains the present value of the FRA product.
*
* This returns explanatory information about the calculation.
*
* @param fra the FRA product for which present value should be computed
* @param provider the rates provider
* @return the explanatory information
*/
public ExplainMap explainPresentValue(ResolvedFra fra, RatesProvider provider) {
ExplainMapBuilder builder = ExplainMap.builder();
Currency currency = fra.getCurrency();
builder.put(ExplainKey.ENTRY_TYPE, "FRA");
builder.put(ExplainKey.PAYMENT_DATE, fra.getPaymentDate());
builder.put(ExplainKey.START_DATE, fra.getStartDate());
builder.put(ExplainKey.END_DATE, fra.getEndDate());
builder.put(ExplainKey.ACCRUAL_YEAR_FRACTION, fra.getYearFraction());
builder.put(ExplainKey.DAYS, (int) DAYS.between(fra.getStartDate(), fra.getEndDate()));
builder.put(ExplainKey.PAYMENT_CURRENCY, currency);
builder.put(ExplainKey.NOTIONAL, CurrencyAmount.of(currency, fra.getNotional()));
builder.put(ExplainKey.TRADE_NOTIONAL, CurrencyAmount.of(currency, fra.getNotional()));
if (fra.getPaymentDate().isBefore(provider.getValuationDate())) {
builder.put(ExplainKey.COMPLETED, Boolean.TRUE);
builder.put(ExplainKey.FORECAST_VALUE, CurrencyAmount.zero(currency));
builder.put(ExplainKey.PRESENT_VALUE, CurrencyAmount.zero(currency));
} else {
double rate = rateComputationFn.explainRate(
fra.getFloatingRate(), fra.getStartDate(), fra.getEndDate(), provider, builder);
builder.put(ExplainKey.FIXED_RATE, fra.getFixedRate());
builder.put(ExplainKey.DISCOUNT_FACTOR, provider.discountFactor(currency, fra.getPaymentDate()));
builder.put(ExplainKey.PAY_OFF_RATE, rate);
builder.put(ExplainKey.UNIT_AMOUNT, unitAmount(fra, provider));
builder.put(ExplainKey.FORECAST_VALUE, forecastValue(fra, provider));
builder.put(ExplainKey.PRESENT_VALUE, presentValue(fra, provider));
}
return builder.build();
}
//-------------------------------------------------------------------------
// calculates the forecast value
private double forecastValue0(ResolvedFra fra, RatesProvider provider) {
if (fra.getPaymentDate().isBefore(provider.getValuationDate())) {
return 0d;
}
// notional * unitAmount
return fra.getNotional() * unitAmount(fra, provider);
}
// unit amount in various discounting methods
private double unitAmount(ResolvedFra fra, RatesProvider provider) {
switch (fra.getDiscounting()) {
case NONE:
return unitAmountNone(fra, provider);
case ISDA:
return unitAmountIsda(fra, provider);
case AFMA:
return unitAmountAfma(fra, provider);
default:
throw new IllegalArgumentException("Unknown FraDiscounting value: " + fra.getDiscounting());
}
}
// NONE discounting method
private double unitAmountNone(ResolvedFra fra, RatesProvider provider) {
double fixedRate = fra.getFixedRate();
double forwardRate = forwardRate(fra, provider);
double yearFraction = fra.getYearFraction();
return (forwardRate - fixedRate) * yearFraction;
}
// ISDA discounting method
private double unitAmountIsda(ResolvedFra fra, RatesProvider provider) {
double fixedRate = fra.getFixedRate();
double forwardRate = forwardRate(fra, provider);
double yearFraction = fra.getYearFraction();
return ((forwardRate - fixedRate) / (1.0 + forwardRate * yearFraction)) * yearFraction;
}
// AFMA discounting method
private double unitAmountAfma(ResolvedFra fra, RatesProvider provider) {
double fixedRate = fra.getFixedRate();
double forwardRate = forwardRate(fra, provider);
double yearFraction = fra.getYearFraction();
return (1.0 / (1.0 + fixedRate * yearFraction)) - (1.0 / (1.0 + forwardRate * yearFraction));
}
//-------------------------------------------------------------------------
// determine the derivative
private double derivative(ResolvedFra fra, RatesProvider provider) {
switch (fra.getDiscounting()) {
case NONE:
return derivativeNone(fra, provider);
case ISDA:
return derivativeIsda(fra, provider);
case AFMA:
return derivativeAfma(fra, provider);
default:
throw new IllegalArgumentException("Unknown FraDiscounting value: " + fra.getDiscounting());
}
}
// NONE discounting method
private double derivativeNone(ResolvedFra fra, RatesProvider provider) {
return fra.getYearFraction();
}
// ISDA discounting method
private double derivativeIsda(ResolvedFra fra, RatesProvider provider) {
double fixedRate = fra.getFixedRate();
double forwardRate = forwardRate(fra, provider);
double yearFraction = fra.getYearFraction();
double dsc = 1.0 / (1.0 + forwardRate * yearFraction);
return (1.0 + fixedRate * yearFraction) * yearFraction * dsc * dsc;
}
// AFMA discounting method
private double derivativeAfma(ResolvedFra fra, RatesProvider provider) {
double forwardRate = forwardRate(fra, provider);
double yearFraction = fra.getYearFraction();
double dsc = 1.0 / (1.0 + forwardRate * yearFraction);
return yearFraction * dsc * dsc;
}
//-------------------------------------------------------------------------
// query the forward rate
private double forwardRate(ResolvedFra fra, RatesProvider provider) {
return rateComputationFn.rate(fra.getFloatingRate(), fra.getStartDate(), fra.getEndDate(), provider);
}
// query the sensitivity
private PointSensitivityBuilder forwardRateSensitivity(ResolvedFra fra, RatesProvider provider) {
return rateComputationFn.rateSensitivity(fra.getFloatingRate(), fra.getStartDate(), fra.getEndDate(), provider);
}
}