/*
Copyright (C) 2008 Richard Gomes
This source code is release under the BSD License.
This file is part of JQuantLib, a free-software/open-source library
for financial quantitative analysts and developers - http://jquantlib.org/
JQuantLib is free software: you can redistribute it and/or modify it
under the terms of the JQuantLib license. You should have received a
copy of the license along with this program; if not, please email
. The license is also available online at
.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the license for more details.
JQuantLib is based on QuantLib. http://quantlib.org/
When applicable, the original copyright notice follows this notice.
*/
/*
Copyright (C) 2005, 2006, 2007 StatPro Italia srl
This file is part of QuantLib, a free-software/open-source library
for financial quantitative analysts and developers - http://quantlib.org/
QuantLib is free software: you can redistribute it and/or modify it
under the terms of the QuantLib license. You should have received a
copy of the license along with this program; if not, please email
. The license is also available online at
.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the license for more details.
*/
package org.jquantlib.termstructures.yieldcurves;
import java.lang.reflect.Constructor;
import java.util.List;
import org.jquantlib.QL;
import org.jquantlib.daycounters.DayCounter;
import org.jquantlib.lang.exceptions.LibraryException;
import org.jquantlib.math.interpolations.Interpolation;
import org.jquantlib.math.interpolations.Interpolation.Interpolator;
import org.jquantlib.quotes.Handle;
import org.jquantlib.quotes.Quote;
import org.jquantlib.termstructures.Bootstrap;
import org.jquantlib.termstructures.Compounding;
import org.jquantlib.termstructures.InterestRate;
import org.jquantlib.termstructures.IterativeBootstrap;
import org.jquantlib.termstructures.RateHelper;
import org.jquantlib.time.Calendar;
import org.jquantlib.time.Date;
import org.jquantlib.time.Frequency;
import org.jquantlib.time.Month;
import org.jquantlib.time.Period;
import org.jquantlib.util.LazyObject;
import org.jquantlib.util.Pair;
/**
* Piecewise yield term structure
*
* This term structure is bootstrapped on a number of interest rate instruments which are passed as a vector of handles to
* RateHelper instances. Their maturities mark the boundaries of the interpolated segments.
*
* Each segment is determined sequentially starting from the earliest period to the latest and is chosen so that the instrument
* whose maturity marks the end of such segment is correctly repriced on the curve.
*
* @note The bootstrapping algorithm will raise an exception if any two instruments have the same maturity date.
*
*
* @category yieldtermstructures
*
* @author Richard Gomes
*/
public class PiecewiseYieldCurve<
T extends Traits,
I extends Interpolator,
B extends Bootstrap>
extends LazyObject implements PiecewiseCurve {
//=============================================================================================
// Translation Notes //
// //
// We are purposely diverging from QuantLib in regards to usage of std::vector. //
// //
// The normal way of translating std::vector would be employing List but, in the //
// specific case of PiecewiseYieldCurve, it would impose more complexity and would require //
// data transformations which would directly impact performance. On the other hand, benefits //
// provided by Lists are not necessary here because only indexed access is required, nothing //
// else. //
// //
// Richard Gomes 14-FEB-2011 //
//=============================================================================================
//
// private final fields
//
private final Class classT;
private final Class classI;
private final Class classB;
private final Traits.Curve baseCurve;
private final RateHelper[] instruments;
private final Handle[] jumps;
private final double accuracy;
private /*@Time*/ double[] jumpTimes;
private Date[] jumpDates;
private Date latestReference;
//
// package private fields
//
private final Traits traits;
private final Interpolator interpolator;
private final Bootstrap bootstrap;
// *********************************************************
// * Case 1 : initByReference with Formal Class Parameters *
// *********************************************************
public PiecewiseYieldCurve(
final Class classT,
final Class classI,
final Class classB,
//--
final Date referenceDate,
final RateHelper[] instruments,
final DayCounter dayCounter) {
this( classT, classI, classB,
referenceDate, instruments, dayCounter,
new Handle/**/[0],
new Date[0],
1.0e-12,
constructInterpolator(classI),
new IterativeBootstrap>(PiecewiseYieldCurve.class)
);
}
public PiecewiseYieldCurve(
final Class classT,
final Class classI,
final Class classB,
//--
final Date referenceDate,
final RateHelper[] instruments,
final DayCounter dayCounter,
//----
final Handle[] jumps) {
this( classT, classI, classB,
referenceDate, instruments, dayCounter,
jumps,
new Date[0],
1.0e-12,
constructInterpolator(classI),
new IterativeBootstrap>(PiecewiseYieldCurve.class)
);
}
public PiecewiseYieldCurve(
final Class classT,
final Class classI,
final Class classB,
//--
final Date referenceDate,
final RateHelper[] instruments,
final DayCounter dayCounter,
//----
final Handle[] jumps,
final Date[] jumpDates) {
this( classT, classI, classB,
referenceDate, instruments, dayCounter,
jumps,
jumpDates,
1.0e-12,
constructInterpolator(classI),
new IterativeBootstrap>(PiecewiseYieldCurve.class)
);
}
public PiecewiseYieldCurve(
final Class classT,
final Class classI,
final Class classB,
//--
final Date referenceDate,
final RateHelper[] instruments,
final DayCounter dayCounter,
//----
final Handle[] jumps,
final Date[] jumpDates,
final /*@Real*/ double accuracy) {
this( classT, classI, classB,
referenceDate, instruments, dayCounter,
jumps,
jumpDates,
accuracy,
constructInterpolator(classI),
new IterativeBootstrap>(PiecewiseYieldCurve.class)
);
}
public PiecewiseYieldCurve(
final Class classT,
final Class classI,
final Class classB,
//--
final Date referenceDate,
final RateHelper[] instruments,
final DayCounter dayCounter,
//----
final Handle[] jumps,
final Date[] jumpDates,
final /*@Real*/ double accuracy,
final Interpolator interpolator) {
this( classT, classI, classB,
referenceDate, instruments, dayCounter,
jumps,
jumpDates,
accuracy,
interpolator,
new IterativeBootstrap>(PiecewiseYieldCurve.class)
);
}
public PiecewiseYieldCurve(
final Class classT,
final Class classI,
final Class classB,
//--
final Date referenceDate,
final RateHelper[] instruments,
final DayCounter dayCounter,
//----
final Handle[] jumps,
final Date[] jumpDates,
final /*@Real*/ double accuracy,
final Interpolator interpolator,
final Bootstrap bootstrap) {
QL.validateExperimentalMode();
QL.require(classT!=null, "Generic type for Traits is null");
QL.require(classI!=null, "Generic type for Interpolation is null");
QL.require(classB!=null, "Generic type for Bootstrap is null");
this.classT = classT;
this.classI = classI;
this.classB = classB;
this.interpolator = interpolator==null ? constructInterpolator(classI) : interpolator;
this.bootstrap = bootstrap==null ? constructBootstrap(classB) : bootstrap;
//TODO; validate types of interpolator and bootstrap
// instantiate base class and call super constructor
this.baseCurve = constructBaseClass(classT, classI, referenceDate, dayCounter, this.interpolator);
this.instruments = instruments; // TODO: clone() ?
this.jumps = jumps==null ? new Handle /**/ [0] : jumps;
this.jumpDates = jumpDates==null ? new Date[0] : jumpDates;
this.accuracy = Double.isNaN(accuracy) ? 1.0e-12 : accuracy;
this.traits = constructTraits(classT);
this.jumpTimes = new double[jumpDates.length];
setJumps();
for (final Handle jump : jumps) {
jump.addObserver(this);
}
bootstrap.setup(this);
}
// ********************************************************
// * Case 2 : initByCalendar with Formal Class Parameters *
// ********************************************************
public PiecewiseYieldCurve(
final Class classT,
final Class classI,
final Class classB,
//--
final /*@Natural*/ int settlementDays,
final Calendar calendar,
final RateHelper[] instruments,
final DayCounter dayCounter) {
this(
classT, classI, classB,
settlementDays, calendar, instruments, dayCounter,
new Handle /**/ [0],
new Date[0],
1.0e-12,
constructInterpolator(classI),
new IterativeBootstrap>(PiecewiseYieldCurve.class)
);
}
public PiecewiseYieldCurve(
final Class classT,
final Class classI,
final Class classB,
//--
final /*@Natural*/ int settlementDays,
final Calendar calendar,
final RateHelper[] instruments,
final DayCounter dayCounter,
//----
final Handle[] jumps) {
this(
classT, classI, classB,
settlementDays, calendar, instruments, dayCounter,
jumps,
new Date[0],
1.0e-12,
constructInterpolator(classI),
new IterativeBootstrap>(PiecewiseYieldCurve.class)
);
}
public PiecewiseYieldCurve(
final Class classT,
final Class classI,
final Class classB,
//--
final /*@Natural*/ int settlementDays,
final Calendar calendar,
final RateHelper[] instruments,
final DayCounter dayCounter,
//----
final Handle[] jumps,
final Date[] jumpDates) {
this(
classT, classI, classB,
settlementDays, calendar, instruments, dayCounter,
jumps,
jumpDates,
1.0e-12,
constructInterpolator(classI),
new IterativeBootstrap>(PiecewiseYieldCurve.class)
);
}
public PiecewiseYieldCurve(
final Class classT,
final Class classI,
final Class classB,
//--
final /*@Natural*/ int settlementDays,
final Calendar calendar,
final RateHelper[] instruments,
final DayCounter dayCounter,
//----
final Handle[] jumps,
final Date[] jumpDates,
final /*@Real*/ double accuracy) {
this(
classT, classI, classB,
settlementDays, calendar, instruments, dayCounter,
jumps,
jumpDates,
accuracy,
constructInterpolator(classI),
new IterativeBootstrap>(PiecewiseYieldCurve.class)
);
}
public PiecewiseYieldCurve(
final Class classT,
final Class classI,
final Class classB,
//--
final /*@Natural*/ int settlementDays,
final Calendar calendar,
final RateHelper[] instruments,
final DayCounter dayCounter,
//----
final Handle[] jumps,
final Date[] jumpDates,
final /*@Real*/ double accuracy,
final Interpolator interpolator) {
this(
classT, classI, classB,
settlementDays, calendar, instruments, dayCounter,
jumps,
jumpDates,
accuracy,
interpolator,
new IterativeBootstrap>(PiecewiseYieldCurve.class)
);
}
public PiecewiseYieldCurve(
final Class classT,
final Class classI,
final Class classB,
//--
final /*@Natural*/ int settlementDays,
final Calendar calendar,
final RateHelper[] instruments,
final DayCounter dayCounter,
//----
final Handle[] jumps,
final Date[] jumpDates,
final /*@Real*/ double accuracy,
final Interpolator interpolator,
final Bootstrap bootstrap) {
QL.validateExperimentalMode();
QL.require(classT!=null, "Generic type for Traits is null");
QL.require(classI!=null, "Generic type for Interpolation is null");
QL.require(classB!=null, "Generic type for Bootstrap is null");
this.classT = classT;
this.classI = classI;
this.classB = classB;
this.interpolator = interpolator==null ? constructInterpolator(classI) : interpolator;
this.bootstrap = bootstrap==null ? constructBootstrap(classB) : bootstrap;
//TODO; validate types of interpolator and bootstrap
// instantiate base class and call super constructor
this.baseCurve = constructBaseClass(classT, classI, settlementDays, calendar, dayCounter, this.interpolator);
this.instruments = instruments;
this.jumps = jumps==null ? new Handle /**/ [0] : jumps;
this.jumpDates = jumpDates==null ? new Date[0] : jumpDates;
this.accuracy = Double.isNaN(accuracy) ? 1.0e-12 : accuracy;
this.traits = constructTraits(classT);
this.jumpTimes = new double[jumpDates.length];
setJumps();
for (final Handle jump : jumps) {
jump.addObserver(this);
}
bootstrap.setup(this);
}
static private Traits.Curve constructBaseClass(
final Class> classT,
final Class> classI,
final Date referenceDate,
final DayCounter dayCounter,
final Interpolator interpolator) {
if (classT == Discount.class)
return new InterpolatedDiscountCurve(classI, referenceDate, dayCounter, interpolator);
else if (classT == ForwardRate.class)
return new InterpolatedForwardCurve(classI, referenceDate, dayCounter, interpolator);
else if (classT == ZeroYield.class)
return new InterpolatedZeroCurve(classI, referenceDate, dayCounter, interpolator);
else
throw new LibraryException("only Discount, ForwardRate and ZeroYield are supported"); // TODO: message
}
static private Traits.Curve constructBaseClass(
final Class> classT,
final Class> classI,
final /*@Natural*/ int settlementDays,
final Calendar calendar,
final DayCounter dayCounter,
final Interpolator interpolator) {
if (classT == Discount.class)
return new InterpolatedDiscountCurve(classI, settlementDays, calendar, dayCounter, interpolator);
else if (classT == ForwardRate.class)
return new InterpolatedForwardCurve(classI, settlementDays, calendar, dayCounter, interpolator);
else if (classT == ZeroYield.class)
return new InterpolatedZeroCurve(classI, settlementDays, calendar, dayCounter, interpolator);
else
throw new LibraryException("only Discount, ForwardRate and ZeroYield are supported"); // TODO: message
}
static private Traits constructTraits(final Class> classT) {
if (Traits.class.isAssignableFrom(classT)) {
try {
return (Traits) classT.newInstance();
} catch (final Exception e) {
throw new LibraryException("could not instantiate Traits", e); // TODO: message
}
}
throw new LibraryException("not a Traits"); // TODO: message
}
static private Interpolator constructInterpolator(final Class> classI) {
if (Interpolator.class.isAssignableFrom(classI)) {
try {
return (Interpolator) classI.newInstance();
} catch (final Exception e) {
throw new LibraryException("could not instantiate Interpolator", e); // TODO: message
}
}
throw new LibraryException("not an Interpolator"); // TODO: message
}
static private Bootstrap constructBootstrap(final Class> classB) {
if (Bootstrap.class.isAssignableFrom(classB)) {
try {
final Constructor c = (Constructor) classB.getConstructor(Class.class);
return c.newInstance(PiecewiseCurve.class);
} catch (final Exception e) {
throw new LibraryException("could not instantiate Bootstrap", e); // TODO: message
}
}
throw new LibraryException("not a Bootstrap"); // TODO: message
}
//
// implements PiecewiseCurve
//
@Override
public Traits traits() /* @ReadOnly */ {
return traits;
}
@Override
public Interpolator interpolator() /* @ReadOnly */ {
return interpolator;
}
@Override
public RateHelper[] instruments() /* @ReadOnly */ {
return instruments;
}
@Override
public double accuracy() {
return accuracy;
}
@Override
public Date maxDate() /* @ReadOnly */ {
calculate();
return baseCurve.maxDate();
}
@Override
public double[] times() /* @ReadOnly */ {
calculate();
return baseCurve.times();
}
@Override
public Date[] dates() /* @ReadOnly */ {
calculate();
return baseCurve.dates();
}
@Override
public double[] data() /* @ReadOnly */ {
calculate();
return baseCurve.data();
}
@Override
public List> nodes() /* @ReadOnly */ {
calculate();
return baseCurve.nodes();
}
@Override
public Date[] jumpDates() /* @ReadOnly */ {
calculate();
return baseCurve.dates();
}
@Override
public double[] jumpTimes() /* @ReadOnly */ {
calculate();
return baseCurve.times();
}
@Override
public void setData(final double[] data) {
baseCurve.setData(data);
}
@Override
public void setDates(final Date[] dates) {
baseCurve.setDates(dates);
}
@Override
public void setTimes(final double[] times) {
baseCurve.setTimes(times);
}
@Override
public Interpolation interpolation() {
return baseCurve.interpolation();
}
@Override
public void setInterpolation(final Interpolation interpolation) {
baseCurve.setInterpolation(interpolation);
}
//
// overrides LazyObject
//
@Override
public void update() {
baseCurve.update();
super.update();
if (baseCurve.referenceDate() != latestReference) {
setJumps();
}
}
//
// private methods
//
public /*@DiscountFactor*/ double discountImpl(final /*@Time*/ double t) /* @ReadOnly */ {
calculate();
if (jumps.length > 0) {
/*@DiscountFactor*/ double jumpEffect = 1.0;
for (int i=0; i 0.0 && thisJump <= 1.0, "invalid jump value");
jumpEffect *= thisJump;
}
return jumpEffect * baseCurve.discount(t);
}
return baseCurve.discount(t);
}
public void setJumps() {
final int nJumps = jumps.length;
final Date referenceDate = baseCurve.referenceDate();
if (this.jumpDates.length==0 && jumps.length!=0) { // turn of year dates
this.jumpDates = new Date[nJumps];
this.jumpTimes = new double[nJumps];
for (int i=0; i
Ads help maintain this website.