/*
 Copyright (C) 2008 Richard Gomes
 Copyright (C) 2009 John Nichol

 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.
 */

package org.jquantlib.termstructures;

import org.jquantlib.QL;
import org.jquantlib.daycounters.DayCounter;
import org.jquantlib.lang.exceptions.LibraryException;
import org.jquantlib.time.Date;
import org.jquantlib.time.Frequency;

/**
 * This class encapsulate the interest rate compounding algebra. It manages day-counting conventions, compounding conventions,
 * conversion between different conventions, discount/compound factor calculations, and implied/equivalent rate calculations.
 *
 * @author Richard Gomes
 */
// TODO: Converted rates are checked against known good results
public class InterestRate {

    //
    // private fields
    //

    private final /* @Rate */double rate;
    private DayCounter dc;
    private Compounding compound;
    private boolean freqMakesSense;
    private int freq;

    //
    // public constructors
    //

    /**
     * Default constructor returning a null interest rate.
     *
     * @category constructors
     */
    public InterestRate() {
        this.rate = 0.0;
    }

    /**
     * Standard constructor. Assumes a {@link Frequency}.Annual
     *
     * @param r represents the rate
     * @param dc is a {@link DayCounter}
     *
     * @category constructors
     */
    public InterestRate(final/* @Rate */double r, final DayCounter dc) {
        this(r, dc, Compounding.Continuous);
    }

    /**
     * Standard constructor. Assumes a {@link Frequency}.Annual
     *
     * @param r represents the rate
     * @param dc is a {@link DayCounter}
     * @param comp is a {@link Compounding}
     *
     * @category constructors
     */
    public InterestRate(final/* @Rate */double r, final DayCounter dc, final Compounding comp) {
        this(r, dc, comp, Frequency.Annual);
    }

    /**
     * Standard constructor.
     *
     * @param r represents the rate
     * @param dc is a {@link DayCounter}
     * @param comp is a {@link Compounding}
     * @param freq represents a {@link Frequency}
     *
     * @category constructors
     */
    public InterestRate(final/* @Rate */double r, final DayCounter dc, final Compounding comp, final Frequency freq) {
        this.rate = r;
        this.dc = dc;
        this.compound = comp;
        this.freqMakesSense = false;

        if (this.compound == Compounding.Compounded || this.compound == Compounding.SimpleThenCompounded) {
            freqMakesSense = true;
            QL.require(freq != Frequency.Once && freq != Frequency.NoFrequency , "frequency not allowed for this interest rate"); // TODO: message
            this.freq = freq.toInteger();
        }
    }

    //
    // public methods
    //

    /**
     * @return the compound (a.k.a capitalization) factor implied by the rate compounded at time t.
     *
     * @note Time must be measured using InterestRate's own day counter.
     *
     * @category inspectors
     */
    public final/* @CompoundFactor */double compoundFactor(final/* @Time */double time) {
        /* @Time */final double t = time;
        QL.require(t >= 0.0 , "negative time not allowed"); // TODO: message
        QL.require(!Double.isNaN(rate) , "null interest rate"); // TODO: message

        // TODO: code review :: please verify against QL/C++ code
        // if (rate<0.0) throw new IllegalArgumentException("null interest rate");

        /* @Rate */final double r = rate;

        if (compound == Compounding.Simple) {
            // 1+r*t
            return 1.0 + r * t;
        } else if (compound == Compounding.Compounded) {
            // (1+r/f)^(f*t)
            return Math.pow((1 + r / freq), (freq * t));
        } else if (compound == Compounding.Continuous) {
            // e^(r*t)
            return Math.exp((r * t));
        } else if (compound == Compounding.SimpleThenCompounded) {
            if (t < (1 / (double) freq)) {
                // 1+r*t
                return 1.0 + r * t;
            } else {
                // (1+(r/f))^(f*t)
                return Math.pow((1 + r / freq), (freq * t));
            }
        } else {
            throw new LibraryException("unknown compounding convention"); // TODO: message
        }
    }

    public final double compoundFactor(final Date d1, final Date d2) {
    	return compoundFactor(d1, d2, new Date(), new Date());
    }
    
    public final double compoundFactor(final Date d1, final Date d2, 
    		final Date refStart, final Date refEnd) {
    	/* @Time */double t = dc.yearFraction(d1, d2, refStart, refEnd);
    	return compoundFactor(t);
}

    // --- inspectors

    /**
     * @return the {@link DayCounter}
     *
     * @category inspectors
     */
    public final DayCounter dayCounter() {
        return this.dc;
    }

    /**
     * @return the {@link Compounding}
     *
     * @category inspectors
     */
    public final Compounding compounding() {
        return this.compound;
    }

    /**
     * @return the {@link Frequency}
     *
     * @category inspectors
     */
    public final Frequency frequency() {
        return freqMakesSense ? Frequency.valueOf(this.freq) : Frequency.NoFrequency;
    }

    // --- discount/compound factor calculations

    /**
     * @return discount factor implied by the rate compounded at time t.
     *
     * @param t time must be measured using InterestRate's own day counter.
     *
     * @category discount/compound factor calculations
     */
    public final/* @DiscountFactor */double discountFactor(final/* @Time */double t) {
        /* @DiscountFactor */final double factor = compoundFactor(t);
        return 1.0d / factor;
    }

    /**
     * @return discount factor implied by the rate compounded between two dates
     *
     * @param d1 is the start date
     * @param d2 is the end date
     *
     * @category discount/compound factor calculations
     */
    public final/* @DiscountFactor */double discountFactor(final Date d1, final Date d2) {
        return discountFactor(d1, d2, new Date());
    }

    /**
     * @return compound factor implied by the rate compounded between two dates
     *
     * @param d1 is the start date
     * @param d2 is the end date
     * @param refStart
     *
     * @category discount/compound factor calculations
     */
    public final/* @DiscountFactor */double discountFactor(final Date d1, final Date d2, final Date refStart) {
        return discountFactor(d1, d2, refStart, new Date());
    }

    /**
     * @return the compound (a.k.a capitalization) factor implied by the rate compounded between two dates.
     *
     * @category discount/compound factor calculations
     */
    public final/* @DiscountFactor */double discountFactor(final Date d1, final Date d2, final Date refStart, final Date refEnd) {
        /* @Time */final double t = this.dc.yearFraction(d1, d2, refStart, refEnd);
        return discountFactor(t);
    }

    public final InterestRate equivalentRate(final/* @Time */double t, final Compounding comp) {
        return equivalentRate(t, comp, Frequency.Annual);
    }

    /**
     * Returns equivalent interest rate for a compounding period t. The resulting InterestRate shares the same implicit day-counting
     * rule of the original InterestRate instance.
     *
     * 

* Time must be measured using the InterestRate's own day counter. * * @return equivalent interest rate for a compounding period t. */ public final InterestRate equivalentRate(final/* @Time */double t, final Compounding comp, final Frequency freq) { return impliedRate(compoundFactor(t), t, this.dc, comp, freq); } public final InterestRate equivalentRate(final Date d1, final Date d2, final DayCounter resultDC, final Compounding comp) { return equivalentRate(d1, d2, resultDC, comp, Frequency.Annual); } /** * Returns equivalent rate for a compounding period between two dates. The resulting rate is calculated taking the required * day-counting rule into account. */ public final InterestRate equivalentRate( final Date d1, final Date d2, final DayCounter resultDC, final Compounding comp, final Frequency freq) { QL.require(d1.lt(d2) , "d1 later than or equal to d2"); // TODO: message /* @Time */final double t1 = this.dc.yearFraction(d1, d2); /* @Time */final double t2 = resultDC.yearFraction(d1, d2); return impliedRate(compoundFactor(t1), t2, resultDC, comp, freq); } /** * Implied interest rate for a given compound factor at a given time. The resulting InterestRate has the day-counter provided as * input. * * @note Time must be measured using the day-counter provided as input. */ static public InterestRate impliedRate(final/* @CompoundFactor */double c, final/* @Time */double time, final DayCounter resultDC, final Compounding comp, final Frequency freq) { /* @Time */final double t = time; final double f = freq.toInteger(); QL.require(c > 0.0 , "positive compound factor required"); // TODO: message QL.require(t > 0.0 , "positive time required"); // TODO: message /* @Rate */double rate; switch (comp) { case Simple: // rate = (compound - 1)/time rate = (c - 1) / t; break; case Compounded: // rate = (compound^(1/(f*t))-1)*f rate = (Math.pow(c, (1 / (f * t))) - 1) * f; break; case Continuous: // rate = log(compound)/t rate = Math.log(c) / t; break; case SimpleThenCompounded: if (t <= (1 / f)) { // rate = (compound - 1)/time rate = (c - 1) / t; } else { // rate = (compound^(1/(f*t))-1)*f rate = (Math.pow(c, (1 / (f * t))) - 1) * f; } break; default: throw new LibraryException("unknown compounding convention"); // TODO: message } return new InterestRate(rate, resultDC, comp, freq); } static public InterestRate impliedRate(final/* @CompoundFactor */double compound, final/* @Time */double t, final DayCounter resultDC, final Compounding comp) { return impliedRate(compound, t, resultDC, comp, Frequency.Annual); } static public InterestRate impliedRate(final/* @CompoundFactor */double compound, final Date d1, final Date d2, final DayCounter resultDC, final Compounding comp) { return impliedRate(compound, d1, d2, resultDC, comp, Frequency.Annual); } /** * Implied rate for a given compound factor between two dates. The resulting rate is calculated taking the required day-counting * rule into account. */ static public InterestRate impliedRate(final/* @CompoundFactor */double compound, final Date d1, final Date d2, final DayCounter resultDC, final Compounding comp, final Frequency freq) { QL.require(d1.le(d2) , "d1 later than or equal to d2"); // TODO: message /* @Time */final double t = resultDC.yearFraction(d1, d2); return impliedRate(compound, t, resultDC, comp, freq); } @Override public String toString() { if (rate == 0.0) { return "null interest rate"; } final StringBuilder sb = new StringBuilder(); sb.append(rate).append(' ').append(dc).append(' '); if (compound == Compounding.Simple) { sb.append("simple compounding"); } else if (compound == Compounding.Compounded) { if ((freq == Frequency.NoFrequency.toInteger()) || (freq == Frequency.Once.toInteger())) { throw new IllegalArgumentException(freq + " frequency not allowed for this interest rate"); } else { sb.append(freq + " compounding"); } } else if (compound == Compounding.Continuous) { sb.append("continuous compounding"); } else if (compound == Compounding.SimpleThenCompounded) { if ((freq == Frequency.NoFrequency.toInteger()) || (freq == Frequency.Once.toInteger())) { throw new IllegalArgumentException(freq + " frequency not allowed for this interest rate"); } else { sb.append("simple compounding up to " + (12 / freq) + " months, then " + freq + " compounding"); } } else { throw new LibraryException("unknown compounding convention"); // TODO: message } return sb.toString(); } public final/* @Rate */double rate() { return rate; } }

Ads help maintain this website.