CMSOption.java
/*
* (c) Copyright Christian P. Fries, Germany. Contact: email@christian-fries.de.
*
* Created on 15.02.2004
*/
package net.finmath.montecarlo.interestrate.products;
import net.finmath.exception.CalculationException;
import net.finmath.functions.AnalyticFormulas;
import net.finmath.marketdata.model.curves.ForwardCurve;
import net.finmath.marketdata.products.Swap;
import net.finmath.marketdata.products.SwapAnnuity;
import net.finmath.montecarlo.RandomVariableFromDoubleArray;
import net.finmath.montecarlo.interestrate.TermStructureMonteCarloSimulationModel;
import net.finmath.stochastic.RandomVariable;
import net.finmath.time.TimeDiscretization;
import net.finmath.time.TimeDiscretizationFromArray;
/**
* Implements the valuation of an option on a CMS rate.
*
* @author Christian Fries
* @version 1.1
*/
public class CMSOption extends AbstractTermStructureMonteCarloProduct {
private final double exerciseDate; // Exercise date
private final double[] fixingDates; // Vector of fixing dates (must be sorted)
private final double[] paymentDates; // Vector of payment dates (same length as fixing dates)
private final double[] periodLengths; // Vector of payment dates (same length as fixing dates)
private final double strike; // Vector of strikes
/**
* Create the option on a CMS rate.
*
* @param exerciseDate The exercise date of the option.
* @param fixingDates Vector of fixing dates.
* @param paymentDates Vector of payment dates (must have same length as fixing dates)
* @param periodLengths Vector of period length (must have same length as fixing dates)
* @param strike Strike swap rate.
*/
public CMSOption(
final double exerciseDate,
final double[] fixingDates,
final double[] paymentDates,
final double[] periodLengths,
final double strike) {
super();
this.exerciseDate = exerciseDate;
this.fixingDates = fixingDates;
this.paymentDates = paymentDates;
this.periodLengths = periodLengths;
this.strike = strike;
}
/**
* This method returns the value random variable of the product within the specified model, evaluated at a given evalutationTime.
* Note: For a lattice this is often the value conditional to evalutationTime, for a Monte-Carlo simulation this is the (sum of) value discounted to evaluation time.
* Cashflows prior evaluationTime are not considered.
*
* @param evaluationTime The time on which this products value should be observed.
* @param model The model used to price the product.
* @return The random variable representing the value of the product discounted to evaluation time
* @throws net.finmath.exception.CalculationException Thrown if the valuation fails, specific cause may be available via the <code>cause()</code> method.
*/
@Override
public RandomVariable getValue(final double evaluationTime, final TermStructureMonteCarloSimulationModel model) throws CalculationException {
/*
* Calculate value of the swap at exercise date on each path (beware of perfect forsight - all rates are simulationTime=exerciseDate)
*/
RandomVariable valueFixLeg = new RandomVariableFromDoubleArray(fixingDates[fixingDates.length-1], 0.0);
RandomVariable valueFloatLeg = new RandomVariableFromDoubleArray(paymentDates[paymentDates.length-1], -1.0);
// Calculate the value of the swap by working backward through all periods
for(int period=fixingDates.length-1; period>=0; period--)
{
final double fixingDate = fixingDates[period];
final double paymentDate = paymentDates[period];
final double periodLength = periodLengths != null ? periodLengths[period] : paymentDate - fixingDate;
// Get random variables - note that this is the rate at simulation time = exerciseDate
final RandomVariable libor = model.getForwardRate(exerciseDate, fixingDate, paymentDate);
// Add payment received at end of period
final RandomVariable payoff = new RandomVariableFromDoubleArray(paymentDate, 1.0 * periodLength);
valueFixLeg = valueFixLeg.add(payoff);
// Discount back to beginning of period
valueFloatLeg = valueFloatLeg.discount(libor, periodLength);
valueFixLeg = valueFixLeg.discount(libor, periodLength);
}
valueFloatLeg = valueFloatLeg.add(1.0);
final RandomVariable parSwapRate = valueFloatLeg.div(valueFixLeg);
RandomVariable payoffUnit = new RandomVariableFromDoubleArray(paymentDates[0], periodLengths[0]);
payoffUnit = payoffUnit.discount(model.getForwardRate(exerciseDate, fixingDates[0], paymentDates[0]),paymentDates[0]-fixingDates[0]);
RandomVariable value = parSwapRate.sub(strike).floor(0.0).mult(payoffUnit);
// If the exercise date is not the first periods start date, then discount back to the exercise date (calculate the forward starting swap)
if(fixingDates[0] != exerciseDate) {
final RandomVariable libor = model.getForwardRate(exerciseDate, exerciseDate, fixingDates[0]);
final double periodLength = fixingDates[0] - exerciseDate;
// Discount back to beginning of period
value = value.discount(libor, periodLength);
}
/*
* Calculate value
*/
final RandomVariable numeraire = model.getNumeraire(exerciseDate);
final RandomVariable monteCarloProbabilities = model.getMonteCarloWeights(model.getTimeIndex(exerciseDate));
value = value.div(numeraire).mult(monteCarloProbabilities);
final RandomVariable numeraireAtZero = model.getNumeraire(evaluationTime);
final RandomVariable monteCarloProbabilitiesAtZero = model.getMonteCarloWeights(evaluationTime);
value = value.mult(numeraireAtZero).div(monteCarloProbabilitiesAtZero);
return value;
}
/**
* This method returns the value of the product using a Black-Scholes model for the swap rate with the Hunt-Kennedy convexity adjustment.
* The model is determined by a discount factor curve and a swap rate volatility.
*
* @param forwardCurve The forward curve from which the swap rate is calculated. The discount curve, associated with this forward curve is used for discounting this option.
* @param swaprateVolatility The volatility of the log-swaprate.
* @return Value of this product
*/
public double getValue(final ForwardCurve forwardCurve, final double swaprateVolatility) {
final double[] swapTenor = new double[fixingDates.length+1];
System.arraycopy(fixingDates, 0, swapTenor, 0, fixingDates.length);
swapTenor[swapTenor.length-1] = paymentDates[paymentDates.length-1];
final TimeDiscretization fixTenor = new TimeDiscretizationFromArray(swapTenor);
final TimeDiscretization floatTenor = new TimeDiscretizationFromArray(swapTenor);
final double forwardSwapRate = Swap.getForwardSwapRate(fixTenor, floatTenor, forwardCurve);
final double swapAnnuity = SwapAnnuity.getSwapAnnuity(fixTenor, forwardCurve);
final double payoffUnit = SwapAnnuity.getSwapAnnuity(new TimeDiscretizationFromArray(swapTenor[0], swapTenor[1]), forwardCurve) / (swapTenor[1] - swapTenor[0]);
return AnalyticFormulas.huntKennedyCMSOptionValue(forwardSwapRate, swaprateVolatility, swapAnnuity, exerciseDate, swapTenor[swapTenor.length-1]-swapTenor[0], payoffUnit, strike) * (swapTenor[1] - swapTenor[0]);
}
}