CustomDiscountCurveBuilder.java

	
package org.drip.sample.stretch;

/*
 * Java Imports
 */

import java.util.*;

import org.drip.function.r1tor1.QuadraticRationalShapeControl;
import org.drip.numerical.common.FormatUtil;
import org.drip.spline.basis.*;
import org.drip.spline.params.*;
import org.drip.spline.stretch.*;

/*
 * -*- mode: java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 */

/*!
 * Copyright (C) 2018 Lakshmi Krishnamurthy
 * Copyright (C) 2017 Lakshmi Krishnamurthy
 * Copyright (C) 2016 Lakshmi Krishnamurthy
 * Copyright (C) 2015 Lakshmi Krishnamurthy
 * Copyright (C) 2014 Lakshmi Krishnamurthy
 * Copyright (C) 2013 Lakshmi Krishnamurthy
 * 
 *  This file is part of DRIP, a free-software/open-source library for buy/side financial/trading model
 *  	libraries targeting analysts and developers
 *  	https://lakshmidrip.github.io/DRIP/
 *  
 *  DRIP is composed of four main libraries:
 *  
 *  - DRIP Fixed Income - https://lakshmidrip.github.io/DRIP-Fixed-Income/
 *  - DRIP Asset Allocation - https://lakshmidrip.github.io/DRIP-Asset-Allocation/
 *  - DRIP Numerical Optimizer - https://lakshmidrip.github.io/DRIP-Numerical-Optimizer/
 *  - DRIP Statistical Learning - https://lakshmidrip.github.io/DRIP-Statistical-Learning/
 * 
 *  - DRIP Fixed Income: Library for Instrument/Trading Conventions, Treasury Futures/Options,
 *  	Funding/Forward/Overnight Curves, Multi-Curve Construction/Valuation, Collateral Valuation and XVA
 *  	Metric Generation, Calibration and Hedge Attributions, Statistical Curve Construction, Bond RV
 *  	Metrics, Stochastic Evolution and Option Pricing, Interest Rate Dynamics and Option Pricing, LMM
 *  	Extensions/Calibrations/Greeks, Algorithmic Differentiation, and Asset Backed Models and Analytics.
 * 
 *  - DRIP Asset Allocation: Library for model libraries for MPT framework, Black Litterman Strategy
 *  	Incorporator, Holdings Constraint, and Transaction Costs.
 * 
 *  - DRIP Numerical Optimizer: Library for Numerical Optimization and Spline Functionality.
 * 
 *  - DRIP Statistical Learning: Library for Statistical Evaluation and Machine Learning.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *   	you may not use this file except in compliance with the License.
 *   
 *  You may obtain a copy of the License at
 *  	http://www.apache.org/licenses/LICENSE-2.0
 *  
 *  Unless required by applicable law or agreed to in writing, software
 *  	distributed under the License is distributed on an "AS IS" BASIS,
 *  	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  
 *  See the License for the specific language governing permissions and
 *  	limitations under the License.
 */

/**
 * CustomDiscountCurveBuilder contains samples that demo how to build a discount curve from purely the cash
 *  flows. It provides for elaborate curve builder control, both at the segment level and at the Stretch
 *  level. In particular, it shows the following:
 * 	- Construct a discount curve from the discount factors available purely from the cash and the euro-dollar
 *  	instruments.
 * 	- Construct a discount curve from the cash flows available from the swap instruments.
 * 
 * In addition, the sample demonstrates the following ways of controlling curve construction:
 * 	- Control over the type of segment basis spline
 * 	- Control over the polynomial basis spline order, Ck, and tension parameters
 * 	- Provision of custom shape controllers (in this case rational shape controller)
 * 	- Calculation of segment monotonicity and convexity
 * 
 * @author Lakshmi Krishnamurthy
 */

public class CustomDiscountCurveBuilder {

	/*
	 * Sample API demonstrating the creation of the segment builder parameters based on Koch-Lyche-Kvasov tension spline.
	 * 
	 *  	USE WITH CARE: This sample ignores errors and does not handle exceptions.
	 */

	private static final SegmentCustomBuilderControl MakeKLKTensionSCBC (
		final double dblTension)
		throws Exception
	{
		return new SegmentCustomBuilderControl (
			MultiSegmentSequenceBuilder.BASIS_SPLINE_KLK_HYPERBOLIC_TENSION, // Spline Type KLK Hyperbolic Basis Tension
			new ExponentialTensionSetParams (dblTension), // Segment Tension Parameter Value
			SegmentInelasticDesignControl.Create (2, 2), // Ck = 2; Curvature penalty (if necessary) order: 2
			new ResponseScalingShapeControl (
				true,
				new QuadraticRationalShapeControl (0.0)), // Univariate Rational Shape Controller
			null
		);
	}

	/*
	 * Sample API demonstrating the creation of the segment builder parameters based on polynomial spline.
	 * 
	 *  	USE WITH CARE: This sample ignores errors and does not handle exceptions.
	 */

	public static final SegmentCustomBuilderControl MakePolynomialSBP (
		final int iNumDegree)
		throws Exception
	{
		return new SegmentCustomBuilderControl (
			MultiSegmentSequenceBuilder.BASIS_SPLINE_POLYNOMIAL, // Spline Type Polynomial
			new PolynomialFunctionSetParams (iNumDegree + 1), // Polynomial of degree (i.e, cubic would be 3+1; 4 basis functions - 1 "intercept")
			SegmentInelasticDesignControl.Create (2, 2), // Ck = 2; Curvature penalty (if necessary) order: 2
			new ResponseScalingShapeControl (
				true,
				new QuadraticRationalShapeControl (0.0)), // Univariate Rational Shape Controller
			null
		);
	}

	/*
	 * Sample API demonstrating the creation of the segment builder parameters
	 * 
	 *  	USE WITH CARE: This sample ignores errors and does not handle exceptions.
	 */

	private static final SegmentCustomBuilderControl MakeSCBC (
		final String strBasisSpline)
		throws Exception
	{
		if (strBasisSpline.equalsIgnoreCase (MultiSegmentSequenceBuilder.BASIS_SPLINE_POLYNOMIAL)) // Polynomial Basis Spline
			return new SegmentCustomBuilderControl (
				MultiSegmentSequenceBuilder.BASIS_SPLINE_POLYNOMIAL, // Spline Type Polynomial
				new PolynomialFunctionSetParams (4), // Polynomial of order 3 (i.e, cubic - 4 basis functions - 1 "intercept")
				SegmentInelasticDesignControl.Create (2, 2), // Ck = 2; Curvature penalty (if necessary) order: 2
				new ResponseScalingShapeControl (
					true,
					new QuadraticRationalShapeControl (0.0)), // Univariate Rational Shape Controller
				null
			);

		if (strBasisSpline.equalsIgnoreCase (MultiSegmentSequenceBuilder.BASIS_SPLINE_EXPONENTIAL_TENSION)) // Exponential Tension Basis Spline
			return new SegmentCustomBuilderControl (
				MultiSegmentSequenceBuilder.BASIS_SPLINE_EXPONENTIAL_TENSION, // Spline Type Exponential Basis Tension
				new ExponentialTensionSetParams (1.), // Segment Tension Parameter Value = 1.
				SegmentInelasticDesignControl.Create (2, 2), // Ck = 2; Curvature penalty (if necessary) order: 2
				new ResponseScalingShapeControl (
					true,
					new QuadraticRationalShapeControl (0.0)), // Univariate Rational Shape Controller
				null
			);

		return null;
	}

	/*
	 * Generate the sample Swap Cash Flows to a given maturity, for the frequency/coupon.
	 * 	Cash Flow is in the form of <Date, Cash Amount> Map.
	 * 
	 *  	USE WITH CARE: This sample ignores errors and does not handle exceptions.
	 */

	private static final TreeMap<Double, Double> SwapCashFlow (
		final double dblCoupon,
		final int iFreq,
		final double dblTenorInYears)
	{
		TreeMap<Double, Double> mapCF = new TreeMap<Double, Double>();

		for (double dblCFDate = 1. / iFreq; dblCFDate < dblTenorInYears; dblCFDate += 1. / iFreq)
			mapCF.put (
				dblCFDate,
				dblCoupon / iFreq
			);

		mapCF.put (
			0.,
			-1.
		);

		mapCF.put (
			1. * dblTenorInYears,
			1. + dblCoupon / iFreq
		);

		return mapCF;
	}

	/**
	 * Generate the DRIP linear constraint corresponding to an exclusive swap segment. This constraint is
	 * 	used to calibrate the discount curve in this segment.
	 *  
	 *  	USE WITH CARE: This sample ignores errors and does not handle exceptions.
	 */

	private static final SegmentResponseValueConstraint GenerateSegmentConstraint (
		final TreeMap<Double, Double> mapCF,
		final MultiSegmentSequence mssDF)
		throws Exception
	{
		double dblValue = 0.;

		List<Double> lsTime = new ArrayList<Double>();

		List<Double> lsWeight = new ArrayList<Double>();

		for (Map.Entry<Double, Double> me : mapCF.entrySet()) {
			double dblTime = me.getKey();

			if (null != mssDF && mssDF.in (dblTime))
				dblValue += mssDF.responseValue (dblTime) * me.getValue();
			else {
				lsTime.add (me.getKey());

				lsWeight.add (me.getValue());
			}
		}

		int iSize = lsTime.size();

		double[] adblNode = new double[iSize];
		double[] adblNodeWeight = new double[iSize];

		for (int i = 0; i < iSize; ++i) {
			adblNode[i] = lsTime.get (i);

			adblNodeWeight[i] = lsWeight.get (i);
		}

		return new SegmentResponseValueConstraint (
			adblNode,
			adblNodeWeight,
			-dblValue
		);
	}

	/**
	 * The set of Par Swap Quotes.
	 * 
	 *  	USE WITH CARE: This sample ignores errors and does not handle exceptions.
	 */

	private static final Map<Double, Double> SwapQuotes()
	{
		Map<Double, Double> mapSwapQuotes = new TreeMap<Double, Double>();

		mapSwapQuotes.put (4., 0.0166);

		mapSwapQuotes.put (5., 0.0206);

		mapSwapQuotes.put (6., 0.0241);

		mapSwapQuotes.put (7., 0.0269);

		mapSwapQuotes.put (8., 0.0292);

		mapSwapQuotes.put (9., 0.0311);

		mapSwapQuotes.put (10., 0.0326);

		mapSwapQuotes.put (11., 0.0340);

		mapSwapQuotes.put (12., 0.0351);

		mapSwapQuotes.put (15., 0.0375);

		mapSwapQuotes.put (20., 0.0393);

		mapSwapQuotes.put (25., 0.0402);

		mapSwapQuotes.put (30., 0.0407);

		mapSwapQuotes.put (40., 0.0409);

		mapSwapQuotes.put (50., 0.0409);

		return mapSwapQuotes;
	}

	/**
	 * Sample Function illustrating the construction of the discount curve off of swap cash flows and
	 *  detailed segment level controls for the swap instruments.Further, the Segment Builder Parameters
	 *  for the cash/swap bridging stretch shown here illustrate using an exponential/hyperbolic spline with
	 *  very high tension (100000.) to "stitch" the cash stretch with the swaps Stretch.
	 * 
	 * Each of the respective stretches have their own tension settings, so the "high" tension
	 *  ensures that there is no propagation of derivatives and therefore high locality.
	 *  
	 *  	USE WITH CARE: This sample ignores errors and does not handle exceptions.
	 */

	private static final MultiSegmentSequence BuildSwapCurve (
		MultiSegmentSequence mss,
		final BoundarySettings bs,
		final int iCalibrationDetail)
		throws Exception
	{
		boolean bFirstNode = true;

		/*
		 * Iterate through the swap instruments and their quotes.
		 */

		for (Map.Entry<Double, Double> meSwapQuote : SwapQuotes().entrySet()) {
			double dblTenorInYears = meSwapQuote.getKey(); // Swap Maturity in Years

			double dblQuote = meSwapQuote.getValue(); // Par Swap Quote

			/*
			 * Generate the Cash flow for the swap Instrument
			 */

			TreeMap<Double, Double> mapCF = SwapCashFlow (
				dblQuote,
				2,
				dblTenorInYears
			);

			/*
			 * Convert the Cash flow into a DRIP segment constraint using the "prior" curve stretch
			 */

			SegmentResponseValueConstraint srvc = GenerateSegmentConstraint (
				mapCF,
				mss
			);

			/*
			 * If it is the head segment, create a stretch instance for the discount curve.
			 */

			if (null == mss) {
				/*
				 * Set the Segment Builder Parameters. This may be set on a segment-by-segment basis.
				 */

				SegmentCustomBuilderControl scbc = MakeSCBC (MultiSegmentSequenceBuilder.BASIS_SPLINE_EXPONENTIAL_TENSION);

				/*
				 * Start off with a single segment stretch, with the corresponding Builder Parameters
				 */

				mss = MultiSegmentSequenceBuilder.CreateUncalibratedStretchEstimator (
					"SWAP",
					new double[] {0., dblTenorInYears},
					new SegmentCustomBuilderControl[] {scbc}
				);

				/*
				 * Set the stretch up by carrying out a "Natural Boundary" Spline Calibration
				 */

				mss.setup (
					1.,
					new SegmentResponseValueConstraint[] {srvc},
					null,
					bs,
					iCalibrationDetail
				);
			} else {
				/*
				 * The Segment Builder Parameters shown here illustrate using an exponential/hyperbolic
				 *  spline with high tension (15.) to "stitch" the cash stretch with the swaps stretch.
				 *  
				 * Each of the respective stretches have their own tension settings, so the "high" tension
				 *  ensures that there is no propagation of derivatives and therefore high locality.
				 */

				SegmentCustomBuilderControl scbcLocal = null;

				if (bFirstNode) {
					bFirstNode = false;

					scbcLocal = MakeKLKTensionSCBC (1.);
				} else
					scbcLocal = MakeKLKTensionSCBC (1.);

				/*
				 * If not the head segment, just append the exclusive swap instrument segment to the tail of
				 * 	the current stretch state, using the constraint generated from the swap cash flow.
				 */

				mss = org.drip.spline.stretch.MultiSegmentSequenceModifier.AppendSegment (
					mss,
					dblTenorInYears,
					srvc,
					scbcLocal,
					bs,
					iCalibrationDetail
				);
			}
		}

		return mss;
	}

	/**
	 * The set of Cash Discount Factors.
	 * 
	 *  	USE WITH CARE: This sample ignores errors and does not handle exceptions.
	 */

	private static final Map<Double, Double> CashDFQuotes()
	{
		Map<Double, Double> mapDFCashQuotes = new TreeMap<Double, Double>();

		mapDFCashQuotes.put (0.005556, 0.999991);

		mapDFCashQuotes.put (0.019444, 0.999967);

		mapDFCashQuotes.put (0.038889, 0.999931);

		mapDFCashQuotes.put (0.083333, 0.999836);

		mapDFCashQuotes.put (0.166667, 0.999622);

		mapDFCashQuotes.put (0.250000, 0.999360);

		mapDFCashQuotes.put (0.500000, 0.998686);

		mapDFCashQuotes.put (0.750000, 0.997888);

		mapDFCashQuotes.put (1.000000, 0.996866);

		mapDFCashQuotes.put (1.250000, 0.995522);

		mapDFCashQuotes.put (1.500000, 0.993609);

		mapDFCashQuotes.put (1.750000, 0.991033);

		mapDFCashQuotes.put (2.000000, 0.987724);

		mapDFCashQuotes.put (2.250000, 0.983789);

		return mapDFCashQuotes;
	}

	/**
	 * Sample Function illustrating the construction of the discount curve off of discount factors and
	 *  detailed segment level controls for the cash instruments.
	 * 
	 *  	USE WITH CARE: This sample ignores errors and does not handle exceptions.
	 */

	private static final MultiSegmentSequence BuildCashCurve (
		final org.drip.spline.stretch.BoundarySettings bs,
		final int iCalibrationDetail)
		throws Exception
	{
		/*
		 * For the head segment, create a calibrated stretch instance for the discount curve.
		 */

		MultiSegmentSequence mssCash = MultiSegmentSequenceBuilder.CreateCalibratedStretchEstimator (
			"CASH",
			new double[] {0., 0.002778}, // t0 and t1 for the segment
			new double[] {1., 0.999996}, // the corresponding discount factors
			new SegmentCustomBuilderControl[] {
				// MakeSCBC (MultiSegmentSequenceBuilder.BASIS_SPLINE_EXPONENTIAL_TENSION)
				MakeKLKTensionSCBC (1.)
			}, // Exponential Tension Basis Spline
			null,
			bs,
			iCalibrationDetail // "Natural" Spline Boundary Condition + Calibrate the full stretch
		);

		/*
		 * Construct the discount curve by iterating through the cash instruments and their discount
		 * 	factors, and inserting them as "knots" onto the existing stretch.
		 */

		for (Map.Entry<Double, Double> meCashDFQuote : CashDFQuotes().entrySet()) {
			double dblTenorInYears = meCashDFQuote.getKey(); // Instrument Tenor in Years

			double dblDF = meCashDFQuote.getValue(); // Discount Factor

			/*
			 * Insert the instrument/quote as a "knot" entity into the stretch. Given the "natural" spline
			 */

			mssCash = MultiSegmentSequenceModifier.InsertKnot (
				mssCash,
				dblTenorInYears,
				dblDF,
				bs,
				iCalibrationDetail
			);
		}

		return mssCash;
	}

	/*
	 * This sample demonstrates the usage construction and usage of Custom Curve Building. It shows the following:
	 * 	- Construct the Cash Curve Sequence with the Standard Natural Boundary Condition.
	 * 	- Construct the Cash Curve Sequence with the Standard Financial Boundary Condition.
	 * 	- Construct the Cash Curve Sequence with the Standard Not-A-Knot Boundary Condition.
	 * 	- Display the DF and the monotonicity for the cash instruments.
	 * 	- Construct the Swap Curve Sequence with the Standard Natural Boundary Condition.
	 * 	- Construct the Swap Curve Sequence with the Standard Financial Boundary Condition.
	 * 	- Construct the Swap Curve Sequence with the Standard Not-A-Knot Boundary Condition.
	 * 	- Display the DF and the monotonicity for the swap instruments.
	 */

	private static final void CustomCurveBuilderTest()
		throws Exception
	{
		/*
		 * Construct the Cash Curve Sequence with the Standard Natural Boundary Condition
		 */

		MultiSegmentSequence mssNaturalCash = BuildCashCurve (
			BoundarySettings.NaturalStandard(),
			MultiSegmentSequence.CALIBRATE
		);

		/*
		 * Construct the Cash Curve Sequence with the Standard Financial Boundary Condition
		 */

		MultiSegmentSequence mssFinancialCash = BuildCashCurve (
			BoundarySettings.FinancialStandard(),
			MultiSegmentSequence.CALIBRATE
		);

		/*
		 * Construct the Cash Curve Sequence with the Standard Not-A-Knot Boundary Condition
		 */

		MultiSegmentSequence mssNotAKnotCash = BuildCashCurve (
			BoundarySettings.NotAKnotStandard (1, 1),
			MultiSegmentSequence.CALIBRATE
		);

		double dblXShift = 0.1 * (mssNaturalCash.getRightPredictorOrdinateEdge() - mssNaturalCash.getLeftPredictorOrdinateEdge());

		System.out.println ("\n\t\t\t----------------       <====>    ------------------       <====>    ------------------");

		System.out.println ("\t\t\tNATURAL BOUNDARY       <====>   NOT A KNOT BOUNDARY       <====>    FINANCIAL BOUNDARY");

		System.out.println ("\t\t\t----------------       <====>    ------------------       <====>    ------------------\n");

		/*
		 * Display the DF and the monotonicity for the cash instruments.
		 */

		for (double dblX = mssNaturalCash.getLeftPredictorOrdinateEdge(); dblX <= mssNaturalCash.getRightPredictorOrdinateEdge(); dblX = dblX + dblXShift)
			System.out.println ("Cash DF[" +
				FormatUtil.FormatDouble (dblX, 1, 3, 1.) + "Y] => " +
				FormatUtil.FormatDouble (mssNaturalCash.responseValue (dblX), 1, 6, 1.) + " | " +
				mssNaturalCash.monotoneType (dblX) + "  <====>  " +
				FormatUtil.FormatDouble (mssNotAKnotCash.responseValue (dblX), 1, 6, 1.) + " | " +
				mssNotAKnotCash.monotoneType (dblX) + "  <====>  " +
				FormatUtil.FormatDouble (mssFinancialCash.responseValue (dblX), 1, 6, 1.) + " | " +
				mssNaturalCash.monotoneType (dblX));

		System.out.println ("\n");

		/*
		 * Construct the Swap Curve Sequence with the Standard Natural Boundary Condition
		 */

		MultiSegmentSequence mssNaturalSwap = BuildSwapCurve (
			mssNaturalCash,
			BoundarySettings.NaturalStandard(),
			MultiSegmentSequence.CALIBRATE
		);

		/*
		 * Construct the Swap Curve Sequence with the Standard Financial Boundary Condition
		 */

		MultiSegmentSequence mssFinancialSwap = BuildSwapCurve (
			mssFinancialCash,
			BoundarySettings.FinancialStandard(),
			MultiSegmentSequence.CALIBRATE
		);

		/*
		 * Construct the Swap Curve Sequence with the Standard Not-A-Knot Boundary Condition
		 */

		MultiSegmentSequence mssNotAKnotSwap = BuildSwapCurve (
			mssNotAKnotCash,
			BoundarySettings.NotAKnotStandard (1, 1),
			MultiSegmentSequence.CALIBRATE
		);

		/*
		 * Display the DF and the monotonicity for the swaps.
		 */

		dblXShift = 0.05 * (mssNaturalSwap.getRightPredictorOrdinateEdge() - mssNaturalSwap.getLeftPredictorOrdinateEdge());

		for (double dblX = mssNaturalSwap.getLeftPredictorOrdinateEdge(); dblX <= mssNaturalSwap.getRightPredictorOrdinateEdge(); dblX = dblX + dblXShift)
			System.out.println (
				"Swap DF   [" +
				FormatUtil.FormatDouble (dblX, 2, 0, 1.) + "Y] => " +
				FormatUtil.FormatDouble (mssNaturalSwap.responseValue (dblX), 1, 6, 1.) + " | " +
				mssNaturalSwap.monotoneType (dblX) + "  <====>  " +
				FormatUtil.FormatDouble (mssNotAKnotSwap.responseValue (dblX), 1, 6, 1.) + " | " +
				mssNotAKnotSwap.monotoneType (dblX) + "  <====>  " +
				FormatUtil.FormatDouble (mssFinancialSwap.responseValue (dblX), 1, 6, 1.) + " | " +
				mssFinancialSwap.monotoneType (dblX)
			);
	}

	public static final void main (
		final String[] astrArgs)
		throws Exception
	{
		CustomCurveBuilderTest();
	}
}