/*
* (c) Copyright Christian P. Fries, Germany. All rights reserved. Contact: email@christian-fries.de.
*
* Created on 28.11.2012
*/
package net.finmath.tests.marketdata.curves;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.Vector;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import net.finmath.marketdata.calibration.ParameterObjectInterface;
import net.finmath.marketdata.calibration.Solver;
import net.finmath.marketdata.model.AnalyticModel;
import net.finmath.marketdata.model.AnalyticModelInterface;
import net.finmath.marketdata.model.curves.Curve.ExtrapolationMethod;
import net.finmath.marketdata.model.curves.Curve.InterpolationEntity;
import net.finmath.marketdata.model.curves.Curve.InterpolationMethod;
import net.finmath.marketdata.model.curves.CurveInterface;
import net.finmath.marketdata.model.curves.DiscountCurve;
import net.finmath.marketdata.model.curves.ForwardCurve;
import net.finmath.marketdata.model.curves.ForwardCurveFromDiscountCurve;
import net.finmath.marketdata.model.curves.ForwardCurveInterface;
import net.finmath.marketdata.products.AnalyticProductInterface;
import net.finmath.marketdata.products.Swap;
import net.finmath.optimizer.SolverException;
import net.finmath.time.RegularSchedule;
import net.finmath.time.TimeDiscretization;
/**
* This class makes some basic tests related to the setup, use and calibration of discount curves and forward curve.
*
* @author Christian Fries
*/
@RunWith(Parameterized.class)
public class CalibrationTest {
static final double errorTolerance = 1E-14;
final InterpolationMethod interpolationMethod;
public CalibrationTest(InterpolationMethod interpolationMethod)
{
this.interpolationMethod = interpolationMethod;
}
/**
* The parameters for this test, that is an error consisting of
* { numberOfPaths, setup }.
*
* @return Array of parameters.
*/
@Parameters
public static Collection<Object[]> generateData()
{
return Arrays.asList(new Object[][] {
{ InterpolationMethod.LINEAR },
{ InterpolationMethod.CUBIC_SPLINE },
{ InterpolationMethod.AKIMA },
{ InterpolationMethod.AKIMA_CONTINUOUS },
{ InterpolationMethod.HARMONIC_SPLINE },
{ InterpolationMethod.HARMONIC_SPLINE_WITH_MONOTONIC_FILTERING },
});
};
/**
* Run some test using discount curves and forward curves and the solver to create a calibrated model.
*
* @param args Arguments - not used.
* @throws SolverException Thrown if the solver cannot find a solution to the calibration problem.
*/
public static void main(String[] args) throws SolverException {
CalibrationTest calibrationTest = new CalibrationTest(InterpolationMethod.LINEAR);
calibrationTest.testForwardCurveFromDiscountCurve();
calibrationTest.testCurvesAndCalibration();
}
@Test
public void testForwardCurveFromDiscountCurve() {
/*
* CREATING AND USING A DISCOUNT CURVE
*/
// Create a discount curve
DiscountCurve discountCurve = DiscountCurve.createDiscountCurveFromDiscountFactors(
"discountCurve" /* name */,
new double[] {0.0, 1.0, 2.0, 4.0, 5.0} /* maturities */,
new double[] {1.0, 0.95, 0.90, 0.85, 0.80} /* discount factors */
);
// Create a forward curve from that discount curve for semi-annual forward rates
ForwardCurveInterface forwardCurveFromDiscountCurve = new ForwardCurveFromDiscountCurve(
discountCurve.getName() /* name of the discount curve to use */,
null /* reference date: not specified since single curve setup */,
null /* period length: not specified since single curve setup */
);
// A model is a collection of curves (curves and products find other curves by looking up their name in the model)
AnalyticModel model1 = new AnalyticModel(new CurveInterface[] { discountCurve , forwardCurveFromDiscountCurve });
System.out.println("Given a discount curve:");
System.out.println(discountCurve.toString());
// We may ask the forward curve for a forward.
double fixingTime = 1.0;
double periodLength = 0.5;
double forwardRate = forwardCurveFromDiscountCurve.getForward(model1, fixingTime, periodLength);
System.out.println("Semi-annual forward with fixing in " + fixingTime + " calculated from that discount curve is " + forwardRate);
// Check if we have the right value
double forwardRateFromDiscountFactor = (discountCurve.getDiscountFactor(model1, fixingTime) / discountCurve.getDiscountFactor(model1, fixingTime + periodLength) - 1) / periodLength;
Assert.assertTrue(Math.abs(forwardRate - forwardRateFromDiscountFactor) < errorTolerance);
System.out.println("__________________________________________________________________________________________\n");
}
@Test
public void testCurvesAndCalibration() throws SolverException {
/*
* CALIBRATING A CURVE - SINGLE CURVE SETUP
*
* Note: Only maturity > 0 (DiscountCurve) and fixing > 0 (ForwardCurve) are calibration parameters (!)
*/
System.out.println("Calibrating a discount curve from swaps (single-curve/self-discounting).");
// Create a discount curve
DiscountCurve discountCurve = DiscountCurve.createDiscountCurveFromDiscountFactors(
"discountCurve" /* name */,
new double[] {0.0, 1.0, 2.0, 4.0, 5.0} /* maturities */,
new double[] {1.0, 0.95, 0.90, 0.85, 0.80} /* discount factors */,
interpolationMethod,
ExtrapolationMethod.CONSTANT,
InterpolationEntity.LOG_OF_VALUE
);
// Create a forward curve from that discount curve for forward rates
ForwardCurveInterface forwardCurveFromDiscountCurve = new ForwardCurveFromDiscountCurve(
discountCurve.getName() /* name of the discount curve to use */,
null /* reference date: not specified since single curve setup */,
null /* period length: not specified since single curve setup */
);
// Create a collection of objective functions (calibration products)
Vector<AnalyticProductInterface> calibrationProducts1 = new Vector<AnalyticProductInterface>();
calibrationProducts1.add(new Swap(new RegularSchedule(new TimeDiscretization(0.0, 1, 1.0)), null, 0.05, "discountCurve", new RegularSchedule(new TimeDiscretization(0.0, 1, 1.0)), forwardCurveFromDiscountCurve.getName(), 0.0, "discountCurve"));
calibrationProducts1.add(new Swap(new RegularSchedule(new TimeDiscretization(0.0, 2, 1.0)), null, 0.04, "discountCurve", new RegularSchedule(new TimeDiscretization(0.0, 2, 1.0)), forwardCurveFromDiscountCurve.getName(), 0.0, "discountCurve"));
calibrationProducts1.add(new Swap(new RegularSchedule(new TimeDiscretization(0.0, 8, 0.5)), null, 0.03, "discountCurve", new RegularSchedule(new TimeDiscretization(0.0, 8, 0.5)), forwardCurveFromDiscountCurve.getName(), 0.0, "discountCurve"));
calibrationProducts1.add(new Swap(new RegularSchedule(new TimeDiscretization(0.0, 10, 0.5)), null, 0.04, "discountCurve", new RegularSchedule(new TimeDiscretization(0.0, 10, 0.5)), forwardCurveFromDiscountCurve.getName(), 0.0, "discountCurve"));
// A model is a collection of curves (curves and products find other curves by looking up their name in the model)
AnalyticModel model1 = new AnalyticModel(new CurveInterface[] { discountCurve , forwardCurveFromDiscountCurve });
// Create a collection of curves to calibrate
Set<ParameterObjectInterface> curvesToCalibrate1 = new HashSet<ParameterObjectInterface>();
curvesToCalibrate1.add(discountCurve);
// Calibrate the curve
Solver solver1 = new Solver(model1, calibrationProducts1);
AnalyticModelInterface calibratedModel1 = solver1.getCalibratedModel(curvesToCalibrate1);
System.out.println("The solver required " + solver1.getIterations() + " iterations.");
System.out.println("The best fit curve is:");
System.out.println(calibratedModel1.getCurve(discountCurve.getName()).toString());
/*
* The model calibratedModel1 now contains a set af calibrated curves.
* The curves are clones. The model model1 still contains the original curve.
*/
// Calibration check
System.out.println("Calibration check:");
double evaluationTime = 0.0;
double error = 0;
for(int calibrationProductIndex = 0; calibrationProductIndex < calibrationProducts1.size(); calibrationProductIndex++) {
AnalyticProductInterface calibrationProduct = calibrationProducts1.get(calibrationProductIndex);
double calibrationProductValue = calibrationProduct.getValue(evaluationTime, calibratedModel1);
System.out.println("Calibration product " + calibrationProductIndex + ":\t" + calibrationProductValue);
error += calibrationProductValue*calibrationProductValue;
}
error = Math.sqrt(error);
Assert.assertTrue(error < errorTolerance);
System.out.println("__________________________________________________________________________________________\n");
/*
* CALIBRATE A FORWARD CURVE, USING THE GIVEN DISCOUNT CURVE (MULTI-CURVE SETUP)
*
* Note: Only maturity > 0 (DiscountCurve) and fixing > 0 (ForwardCurve) are calibration parameters (!)
*/
// Create initial guess for the curve
ForwardCurve forwardCurve = ForwardCurve.createForwardCurveFromForwards("forwardCurve", new double[] {2.0/365.0, 1.0, 2.0, 3.0, 4.0}, new double[] {0.05, 0.05, 0.05, 0.05, 0.05}, model1, discountCurve.getName(), 0.5);
// Make collection of all curves used in valuation
AnalyticModel model2 = new AnalyticModel( new CurveInterface[] { discountCurve, forwardCurve } );
System.out.println("Calibrating a forward curve from swaps using the given discount curve.");
// Create a collection of objective functions (calibration products)
Vector<AnalyticProductInterface> calibrationProducts2 = new Vector<AnalyticProductInterface>();
// It is possible to mix tenors (although it may not be meaningful in a forward curve calibration)
calibrationProducts2.add(new Swap(new RegularSchedule(new TimeDiscretization(0.0, 1, 1.0)), null, 0.06, "discountCurve", new RegularSchedule(new TimeDiscretization(0.0, 1, 0.5)), "forwardCurve", 0.0, "discountCurve"));
calibrationProducts2.add(new Swap(new RegularSchedule(new TimeDiscretization(0.0, 2, 1.0)), null, 0.05, "discountCurve", new RegularSchedule(new TimeDiscretization(0.0, 2, 0.5)), "forwardCurve", 0.0, "discountCurve"));
calibrationProducts2.add(new Swap(new RegularSchedule(new TimeDiscretization(0.0, 6, 0.5)), null, 0.04, "discountCurve", new RegularSchedule(new TimeDiscretization(0.0, 6, 0.5)), "forwardCurve", 0.0, "discountCurve"));
calibrationProducts2.add(new Swap(new RegularSchedule(new TimeDiscretization(0.0, 8, 0.5)), null, 0.04, "discountCurve", new RegularSchedule(new TimeDiscretization(0.0, 8, 0.5)), "forwardCurve", 0.0, "discountCurve"));
calibrationProducts2.add(new Swap(new RegularSchedule(new TimeDiscretization(0.0, 10, 0.5)), null, 0.04, "discountCurve", new RegularSchedule(new TimeDiscretization(0.0, 10, 0.5)), "forwardCurve", 0.0, "discountCurve"));
// Create a collection of curves to calibrate
Set<ParameterObjectInterface> curvesToCalibrate2 = new HashSet<ParameterObjectInterface>();
curvesToCalibrate2.add(forwardCurve);
// Calibrate the curve
Solver solver2 = new Solver(model2, calibrationProducts2);
AnalyticModelInterface calibratedModel2 = solver2.getCalibratedModel(curvesToCalibrate2);
System.out.println("The solver required " + solver2.getIterations() + " iterations.");
System.out.println("The best fit curve is:");
System.out.println(calibratedModel2.getCurve(forwardCurve.getName()).toString());
/*
* The model calibratedModel2 now contains a set of calibrated curves.
* The curves are clones. The model model2 still contains the original curve.
*/
// Calibration check
System.out.println("Calibration check:");
double error2 = 0;
for(int calibrationProductIndex = 0; calibrationProductIndex < calibrationProducts2.size(); calibrationProductIndex++) {
AnalyticProductInterface calibrationProduct = calibrationProducts2.get(calibrationProductIndex);
double calibrationProductValue = calibrationProduct.getValue(evaluationTime, calibratedModel2);
System.out.println("Calibration product " + calibrationProductIndex + ":\t" + calibrationProductValue);
error2 += calibrationProductValue*calibrationProductValue;
}
error2 = Math.sqrt(error2);
Assert.assertTrue(error2 < errorTolerance);
System.out.println("__________________________________________________________________________________________\n");
}
}