/*
* (c) Copyright Christian P. Fries, Germany. All rights reserved. Contact: email@christian-fries.de.
*
* Created on 10.02.2004
*/
package net.finmath.tests.montecarlo.interestrate;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.time.LocalDate;
import java.time.Month;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.junit.Assert;
import org.junit.Test;
import net.finmath.exception.CalculationException;
import net.finmath.marketdata.model.AnalyticModel;
import net.finmath.marketdata.model.AnalyticModelInterface;
import net.finmath.marketdata.model.curves.DiscountCurve;
import net.finmath.marketdata.model.curves.DiscountCurveInterface;
import net.finmath.marketdata.model.curves.ForwardCurve;
import net.finmath.marketdata.model.curves.ForwardCurveInterface;
import net.finmath.marketdata.model.volatilities.AbstractVolatilitySurface;
import net.finmath.marketdata.model.volatilities.CapletVolatilitiesParametric;
import net.finmath.marketdata.products.Cap;
import net.finmath.montecarlo.interestrate.LIBORMarketModel;
import net.finmath.montecarlo.interestrate.LIBORMarketModelInterface;
import net.finmath.montecarlo.interestrate.LIBORModelMonteCarloSimulation;
import net.finmath.montecarlo.interestrate.LIBORModelMonteCarloSimulationInterface;
import net.finmath.montecarlo.interestrate.modelplugins.LIBORCorrelationModelExponentialDecay;
import net.finmath.montecarlo.interestrate.modelplugins.LIBORCovarianceModelFromVolatilityAndCorrelation;
import net.finmath.montecarlo.interestrate.modelplugins.LIBORVolatilityModelFourParameterExponentialFormIntegrated;
import net.finmath.montecarlo.interestrate.products.FlexiCap;
import net.finmath.montecarlo.process.ProcessEulerScheme;
import net.finmath.time.RegularSchedule;
import net.finmath.time.ScheduleInterface;
import net.finmath.time.TimeDiscretization;
/**
* This class tests the valuation of a Cap using LMM and an AnalyticModel.
*
* @author Christian Fries
*/
public class CapValuationTest {
LocalDate referenceDate = LocalDate.of(2014, Month.JUNE, 15);
private ForwardCurveInterface forwardCurve;
private DiscountCurveInterface discountCurve;
private LIBORModelMonteCarloSimulationInterface liborMarketModel;
private AbstractVolatilitySurface capletVol;
private static DecimalFormat formatterMaturity = new DecimalFormat("00.00", new DecimalFormatSymbols(Locale.ENGLISH));
private static DecimalFormat formatterValue = new DecimalFormat(" ##0.000%;-##0.000%", new DecimalFormatSymbols(Locale.ENGLISH));
private static DecimalFormat formatterDeviation = new DecimalFormat(" 0.00000E00;-0.00000E00", new DecimalFormatSymbols(Locale.ENGLISH));
public CapValuationTest() throws CalculationException {
final int numberOfPaths = 10000;
// Create a libor market model
init(numberOfPaths);
}
/**
* Initialize market data objects and the libor market model object.
*
* @param numberOfPaths Numer of paths of the LIBOR market model.
* @throws CalculationException Thrown if a numerical algorithm fails.
*/
private void init(int numberOfPaths) throws CalculationException {
/*
* Create the libor tenor structure and the initial values
*/
double liborPeriodLength = 0.25;
double liborRateTimeHorzion = 20.0;
TimeDiscretization liborPeriodDiscretization = new TimeDiscretization(0.0, (int) (liborRateTimeHorzion / liborPeriodLength), liborPeriodLength);
// Create the forward curve (initial value of the LIBOR market model)
forwardCurve = ForwardCurve.createForwardCurveFromForwards(
"forwardCurve" /* name of the curve */,
new double[] {0.5 , 1.0 , 2.0 , 5.0 , 40.0} /* fixings of the forward */,
new double[] {0.05, 0.05, 0.05, 0.05, 0.05} /* forwards */,
liborPeriodLength /* tenor / period length */
);
// Create the discount curve
discountCurve = DiscountCurve.createDiscountCurveFromZeroRates(
"discountCurve" /* name of the curve */,
new double[] {0.5 , 1.0 , 2.0 , 5.0 , 40.0} /* maturities */,
new double[] {0.04, 0.04, 0.04, 0.04, 0.05} /* zero rates */
);
// Create the capletVolatilitySurface
double a = 0.25;
double b = 3.00;
double c = 1.50;
double d = 0.10;
capletVol = new CapletVolatilitiesParametric("EUR", referenceDate, a, b, c, d);
// capletVol = new CapletVolatilitiesParametricFourParameterPicewiseConstant("EUR", referenceDate, a, b, c, d, liborPeriodDiscretization);
/*
* Create a simulation time discretization
*/
double lastTime = 20.0;
double dt = 0.25;
TimeDiscretization timeDiscretization = new TimeDiscretization(0.0, (int) (lastTime / dt), dt);
// LIBOR volatility model
LIBORVolatilityModelFourParameterExponentialFormIntegrated volatilityModel = new LIBORVolatilityModelFourParameterExponentialFormIntegrated(timeDiscretization, liborPeriodDiscretization, a, b, c, d, false /* isCalibrateable */);
// LIBORVolatilityModelFourParameterExponentialForm volatilityModel = new LIBORVolatilityModelFourParameterExponentialForm(timeDiscretization, liborPeriodDiscretization, a, b, c, d, false /* isCalibrateable */);
/*
* Create a correlation model rho_{i,j} = exp(-a * abs(T_i-T_j))
*/
int numberOfFactors = 1;
double correlationDecayParam = 0.0;
LIBORCorrelationModelExponentialDecay correlationModel = new LIBORCorrelationModelExponentialDecay(
timeDiscretization, liborPeriodDiscretization, numberOfFactors,
correlationDecayParam);
/*
* Combine volatility model and correlation model to a covariance model
*/
LIBORCovarianceModelFromVolatilityAndCorrelation covarianceModel =
new LIBORCovarianceModelFromVolatilityAndCorrelation(timeDiscretization,
liborPeriodDiscretization, volatilityModel, correlationModel);
// BlendedLocalVolatlityModel (future extension)
// AbstractLIBORCovarianceModel covarianceModel2 = new BlendedLocalVolatlityModel(covarianceModel, 0.00, false);
// Set model properties
Map<String, String> properties = new HashMap<String, String>();
// Choose the simulation measure
properties.put("measure", LIBORMarketModel.Measure.SPOT.name());
// Choose log normal model
properties.put("stateSpace", LIBORMarketModel.StateSpace.LOGNORMAL.name());
// Empty array of calibration items - hence, model will use given covariance
LIBORMarketModel.CalibrationItem[] calibrationItems = new LIBORMarketModel.CalibrationItem[0];
/*
* Create corresponding LIBOR Market Model
*/
LIBORMarketModelInterface liborMarketModel = new LIBORMarketModel(
liborPeriodDiscretization, forwardCurve, discountCurve, covarianceModel, calibrationItems, properties);
ProcessEulerScheme process = new ProcessEulerScheme(
new net.finmath.montecarlo.BrownianMotion(timeDiscretization,
numberOfFactors, numberOfPaths, 3141 /* seed */));
// process.setScheme(ProcessEulerScheme.Scheme.PREDICTOR_CORRECTOR);
this.liborMarketModel = new LIBORModelMonteCarloSimulation(liborMarketModel, process);
}
@Test
public void testCap() throws CalculationException {
/*
* Value a set of caps
*/
System.out.println("Cap prices:\n");
System.out.println("Maturity Simulation Analytic Deviation");
double maxAbsDeviation = 0.0;
for (int maturityIndex = 2; maturityIndex <= liborMarketModel.getNumberOfLibors() - 1; maturityIndex++) {
double maturity = liborMarketModel.getLiborPeriod(maturityIndex);
System.out.print(formatterMaturity.format(maturity) + " ");
double strike = 0.05;
double[] fixingDates = (new TimeDiscretization(0.25, maturityIndex-2, 0.25)).getAsDoubleArray();
double[] paymentDates = (new TimeDiscretization(0.50, maturityIndex-2, 0.25)).getAsDoubleArray();
double[] strikes = new double[maturityIndex-1];
Arrays.fill(strikes, strike);
// Create a digital caplet
FlexiCap cap = new FlexiCap(fixingDates, paymentDates, strikes, Integer.MAX_VALUE);
// Value with Monte Carlo
double valueSimulation = cap.getValue(liborMarketModel);
System.out.print(formatterValue.format(valueSimulation) + " ");
// Value analytic
AnalyticModelInterface model = new AnalyticModel();
model = model.addCurves(forwardCurve);
model = model.addCurves(discountCurve);
model = model.addVolatilitySurfaces(capletVol);
LocalDate startDate = referenceDate.plusMonths(3);
ScheduleInterface schedule = new RegularSchedule(new TimeDiscretization(0.25, maturityIndex-1, 0.25));
// ScheduleInterface schedule = ScheduleGenerator.createScheduleFromConventions(referenceDate.getTime(), startDate.getTime(), "quarterly", (maturityIndex-1)*0.25, "act/365", "first");
Cap capAnalytic = new Cap(schedule, forwardCurve.getName() /* forwardCurveName */, strike, false /* isStrikeMoneyness */, discountCurve.getName() /* discountCurveName */, "EUR" /* volatiltiySufaceName */);
double valueAnalytic = capAnalytic.getValue(model);
System.out.print(formatterValue.format(valueAnalytic) + " ");
// Absolute deviation
double deviation = (valueSimulation - valueAnalytic);
System.out.println(formatterDeviation.format(deviation) + " ");
maxAbsDeviation = Math.max(maxAbsDeviation, Math.abs(deviation));
}
System.out.println("Maximum abs deviation: " + formatterDeviation.format(maxAbsDeviation));
System.out.println("__________________________________________________________________________________________\n");
/*
* jUnit assertion: condition under which we consider this test successful
*/
Assert.assertEquals("Deviation", 0.0, maxAbsDeviation, 3E-3);
}
}