/*
* (c) Copyright Christian P. Fries, Germany. All rights reserved. Contact: email@christian-fries.de.
*
* Created on 10.03.2013
*/
package net.finmath.tests.convexityadjustment;
import java.text.DecimalFormat;
import org.junit.Assert;
import org.junit.Test;
import net.finmath.exception.CalculationException;
import net.finmath.functions.AnalyticFormulas;
import net.finmath.marketdata.model.curves.ForwardCurve;
import net.finmath.marketdata.model.curves.ForwardCurveInterface;
import net.finmath.marketdata.products.Swap;
import net.finmath.marketdata.products.SwapAnnuity;
import net.finmath.montecarlo.BrownianMotion;
import net.finmath.montecarlo.interestrate.LIBORMarketModel;
import net.finmath.montecarlo.interestrate.LIBORModelMonteCarloSimulation;
import net.finmath.montecarlo.interestrate.modelplugins.LIBORCorrelationModelExponentialDecay;
import net.finmath.montecarlo.interestrate.modelplugins.LIBORCovarianceModelFromVolatilityAndCorrelation;
import net.finmath.montecarlo.interestrate.modelplugins.LIBORVolatilityModelTwoParameterExponentialForm;
import net.finmath.montecarlo.interestrate.products.CMSOption;
import net.finmath.montecarlo.interestrate.products.Caplet;
import net.finmath.montecarlo.interestrate.products.Swaption;
import net.finmath.montecarlo.interestrate.products.SwaptionAnalyticApproximation;
import net.finmath.montecarlo.process.ProcessEulerScheme;
import net.finmath.time.TimeDiscretization;
import net.finmath.time.TimeDiscretizationInterface;
public class CMSOptionTest {
// Model properties
private final double initialValue = 0.10;
private final double volatility = 0.10;
private final int numberOfFactors = 5;
private final double correlationDecay = 0.1;
// Process discretization properties
private final int numberOfPaths = 10000;
private final int numberOfTimeSteps = 15;
private final double deltaT = 0.5;
// LIBOR tenor discretization
private final int numberOfPeriods = 30;
private final double periodLength = 0.5;
// Random number generator seed
private final int seed = 3141;
// Java DecimalFormat for our output format
static final DecimalFormat formatterPercent = new DecimalFormat("0.0000%");
@Test
public void testCMSOption() throws CalculationException {
// Create a flat forward rate curve
ForwardCurveInterface forwardCurve = ForwardCurve.createForwardCurveFromForwards("forwardCurve",
new double[] { 0.0, numberOfPeriods*periodLength },
new double[] { initialValue, initialValue },
periodLength);
// Create a LIBOR market model Monte-Carlo simulation
LIBORModelMonteCarloSimulation liborMarketModelMonteCarloSimulation = this.getLIBORModelMonteCarloSimulation(forwardCurve);
double exerciseDate = 5.0;
double[] fixingDates = {5.0, 5.5, 6.0, 6.5, 7.0, 7.5};
double[] paymentDates = {5.5, 6.0, 6.5, 7.0, 7.5, 8.0};
double[] swapTenor = {5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0};
double[] periodLengths = {0.5, 0.5, 0.5, 0.5, 0.5, 0.5};
double strike = 0.100;
double[] swaprates = new double[periodLengths.length];
java.util.Arrays.fill(swaprates, strike);
/*
* Value the CMS Option
*/
// Calculate approximate swaprate volatility from LIBOR market model (analytic).
SwaptionAnalyticApproximation swaptionAnalytic = new SwaptionAnalyticApproximation(strike, swapTenor, SwaptionAnalyticApproximation.ValueUnit.INTEGRATEDVARIANCE);
double swaprateIntegratedVariance = swaptionAnalytic.getValue(liborMarketModelMonteCarloSimulation);
double swaprateVolatility = Math.sqrt(swaprateIntegratedVariance/exerciseDate);
// Create CMS Option
CMSOption cmsOption = new CMSOption(exerciseDate, fixingDates, paymentDates, periodLengths, strike);
// Value using LMM
double valueCMSOptionLMM = cmsOption.getValue(liborMarketModelMonteCarloSimulation);
System.out.println("CMS Option with LIBOR Market Model..........................:\t" + formatterPercent.format(valueCMSOptionLMM));
// Value using analytics model
double valueCMSOptionHK = cmsOption.getValue(forwardCurve, swaprateVolatility);
System.out.println("CMS Option with Hunt-Kennedy/Black-Scholes..................:\t" + formatterPercent.format(valueCMSOptionHK));
// Value using convexity adjusted forward rate in a Black-Scholes formula
TimeDiscretizationInterface fixTenor = new TimeDiscretization(swapTenor);
TimeDiscretizationInterface floatTenor = new TimeDiscretization(swapTenor);
double rate = 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]);
double adjustedCMSRate = AnalyticFormulas.huntKennedyCMSAdjustedRate(rate, swaprateVolatility, swapAnnuity, exerciseDate, swapTenor[swapTenor.length-1]-swapTenor[0], payoffUnit);
double valueCMSOptionHKAdjRate = AnalyticFormulas.blackModelSwaptionValue(adjustedCMSRate, swaprateVolatility, exerciseDate, strike, payoffUnit) * (swapTenor[1]-swapTenor[0]);
System.out.println("CMS Option with Black-Scholes using Adjusted Forward Swapate:\t" + formatterPercent.format(valueCMSOptionHKAdjRate));
System.out.println("\nInfo:");
System.out.println("Forward Swaprate............................................:\t" + formatterPercent.format(rate));
System.out.println("Convexity Adjusted Forward Swaprate (Hunt-Kennedy)..........:\t" + formatterPercent.format(adjustedCMSRate));
/*
* jUnit assertion: condition under which we consider this test successful
*/
Assert.assertEquals("Value", valueCMSOptionLMM, valueCMSOptionHK, 1E-3);
/*
* Value a caplet with same fixing date
*/
Caplet caplet = new Caplet(fixingDates[0], periodLengths[0], strike);
double valueCaplet = caplet.getValue(liborMarketModelMonteCarloSimulation);
System.out.println("Caplet with LIBOR Market Model..............................:\t" + formatterPercent.format(valueCaplet));
/*
* Value a swaption with same swap tenor and exercise date
*/
Swaption swaption = new Swaption(exerciseDate, fixingDates, paymentDates, periodLengths, swaprates);
double swaptionNotional = payoffUnit / swapAnnuity * (swapTenor[1]-swapTenor[0]);
double valueSwp = swaption.getValue(liborMarketModelMonteCarloSimulation);
System.out.println("Swaption with LIBOR Market Model............................:\t" + formatterPercent.format(valueSwp * swaptionNotional));
double valueSwaptionAnalytic = swaption.getValue(forwardCurve, swaprateVolatility);
System.out.println("Swaption with Black-Scholes.................................:\t" + formatterPercent.format(valueSwaptionAnalytic * swaptionNotional));
}
public LIBORModelMonteCarloSimulation getLIBORModelMonteCarloSimulation(ForwardCurveInterface forwardCurve) throws CalculationException {
// Create the time discretization
TimeDiscretizationInterface timeDiscretization = new TimeDiscretization(0.0, numberOfTimeSteps, deltaT);
// Create the tenor discretization
TimeDiscretizationInterface tenorDiscretization = new TimeDiscretization(0.0, numberOfPeriods, periodLength);
/*
* Create LIBOR Market Model
*/
LIBORMarketModel liborMarketModel = new LIBORMarketModel(
tenorDiscretization,
forwardCurve,
new LIBORCovarianceModelFromVolatilityAndCorrelation(
timeDiscretization, tenorDiscretization,
new LIBORVolatilityModelTwoParameterExponentialForm(timeDiscretization, tenorDiscretization, volatility, 0.0),
new LIBORCorrelationModelExponentialDecay(timeDiscretization, tenorDiscretization, numberOfFactors, correlationDecay, false))
);
BrownianMotion brownianMotion = new BrownianMotion(timeDiscretization, numberOfFactors, numberOfPaths, seed);
ProcessEulerScheme process = new ProcessEulerScheme(brownianMotion);
// process.setScheme(ProcessEulerScheme.Scheme.PREDICTOR_CORRECTOR);
LIBORModelMonteCarloSimulation liborMarketModelMonteCarloSimulation = new LIBORModelMonteCarloSimulation(liborMarketModel, process);
return liborMarketModelMonteCarloSimulation;
}
}