/*
* (c) Copyright Christian P. Fries, Germany. All rights reserved. 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.ForwardCurveInterface;
import net.finmath.marketdata.products.Swap;
import net.finmath.marketdata.products.SwapAnnuity;
import net.finmath.montecarlo.RandomVariable;
import net.finmath.montecarlo.interestrate.LIBORModelMonteCarloSimulationInterface;
import net.finmath.stochastic.RandomVariableInterface;
import net.finmath.time.TimeDiscretization;
import net.finmath.time.TimeDiscretizationInterface;
/**
* Implements the valuation of an option on a CMS rate.
*
* @author Christian Fries
* @version 1.1
*/
public class CMSOption extends AbstractLIBORMonteCarloProduct {
private final double exerciseDate; // Exercise date
private final double[] fixingDates; // Vector of fixing dates (must be sorted)
private double[] paymentDates; // Vector of payment dates (same length as fixing dates)
private double[] periodLengths; // Vector of payment dates (same length as fixing dates)
private 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(
double exerciseDate,
double[] fixingDates,
double[] paymentDates,
double[] periodLengths,
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 cause() method.
*/
@Override
public RandomVariableInterface getValue(double evaluationTime, LIBORModelMonteCarloSimulationInterface model) throws CalculationException {
/*
* Calculate value of the swap at exercise date on each path (beware of perfect forsight - all rates are simulationTime=exerciseDate)
*/
RandomVariableInterface valueFixLeg = new RandomVariable(fixingDates[fixingDates.length-1], 0.0);
RandomVariableInterface valueFloatLeg = new RandomVariable(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--)
{
double fixingDate = fixingDates[period];
double paymentDate = paymentDates[period];
double periodLength = periodLengths != null ? periodLengths[period] : paymentDate - fixingDate;
// Get random variables - note that this is the rate at simulation time = exerciseDate
RandomVariableInterface libor = model.getLIBOR(exerciseDate, fixingDate, paymentDate);
// Add payment received at end of period
RandomVariableInterface payoff = new RandomVariable(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);
RandomVariableInterface parSwapRate = valueFloatLeg.div(valueFixLeg);
RandomVariableInterface payoffUnit = new RandomVariable(paymentDates[0], periodLengths[0]);
payoffUnit = payoffUnit.discount(model.getLIBOR(exerciseDate, fixingDates[0], paymentDates[0]),paymentDates[0]-fixingDates[0]);
RandomVariableInterface 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) {
RandomVariableInterface libor = model.getLIBOR(exerciseDate, exerciseDate, fixingDates[0]);
double periodLength = fixingDates[0] - exerciseDate;
// Discount back to beginning of period
value = value.discount(libor, periodLength);
}
/*
* Calculate value
*/
RandomVariableInterface numeraire = model.getNumeraire(exerciseDate);
RandomVariableInterface monteCarloProbabilities = model.getMonteCarloWeights(model.getTimeIndex(exerciseDate));
value = value.div(numeraire).mult(monteCarloProbabilities);
RandomVariableInterface numeraireAtZero = model.getNumeraire(evaluationTime);
RandomVariableInterface 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 disocunting this option.
* @param swaprateVolatility The volatility of the log-swaprate.
* @return Value of this product
*/
public double getValue(ForwardCurveInterface forwardCurve, double swaprateVolatility) {
double[] swapTenor = new double[fixingDates.length+1];
System.arraycopy(fixingDates, 0, swapTenor, 0, fixingDates.length);
swapTenor[swapTenor.length-1] = paymentDates[paymentDates.length-1];
TimeDiscretizationInterface fixTenor = new TimeDiscretization(swapTenor);
TimeDiscretizationInterface floatTenor = new TimeDiscretization(swapTenor);
double forwardSwapRate = Swap.getForwardSwapRate(fixTenor, floatTenor, forwardCurve);
double swapAnnuity = SwapAnnuity.getSwapAnnuity(fixTenor, forwardCurve);
double payoffUnit = SwapAnnuity.getSwapAnnuity(new TimeDiscretization( new double[] { 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]);
}
}
Ads help maintain this website.