com.opengamma.analytics.financial.credit.creditdefaultswap.pricing.vanilla.isdanew.ISDACompliantCurve.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.analytics.financial.credit.creditdefaultswap.pricing.vanilla.isdanew.ISDACompliantCurve.java

Source

/**
 * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
 * 
 * Please see distribution for license.
 */
package com.opengamma.analytics.financial.credit.creditdefaultswap.pricing.vanilla.isdanew;

import java.util.Arrays;

import org.apache.commons.lang.NotImplementedException;

import com.opengamma.util.ArgumentChecker;

/**
 * A yield or hazard curve values between nodes are linearly interpolated from t*r points (where t is time and r is the zero rate)
 */
public class ISDACompliantCurve {

    private final int _n; // number of knots in curve

    // the knot positions and values
    private final double[] _t;
    private final double[] _r;

    // these are simply cached values (they can be recalculated from _t & _r)
    private final double[] _rt;
    private final double[] _df;

    // These are use in the case that the curve is build with a particular base-date but is then 'seen' from a different base-date.
    // They are zero if both base-dates coincide
    private final double _offsetTime;
    private final double _offsetRT;

    /**
     * Flat curve at level r
     * @param t (arbitrary) single knot point (t > 0)
     * @param r the level
     */
    public ISDACompliantCurve(final double t, final double r) {
        this(new double[] { t }, new double[] { r });
    }

    /**
     * 
     * @param t Set of times that form the knots of the curve. Must be ascending with the first value >= 0.
     * @param r Set of zero rates
     */
    public ISDACompliantCurve(final double[] t, final double[] r) {
        ArgumentChecker.notEmpty(t, "t empty");
        ArgumentChecker.notEmpty(r, "r empty");
        _n = t.length;
        ArgumentChecker.isTrue(_n == r.length, "r and t different lengths");
        ArgumentChecker.isTrue(t[0] >= 0, "first t must be >= 0.0");
        for (int i = 1; i < _n; i++) {
            ArgumentChecker.isTrue(t[i] > t[i - 1], "Times must be ascending");
        }

        _t = new double[_n];
        _r = new double[_n];
        _rt = new double[_n];
        _df = new double[_n];
        System.arraycopy(t, 0, _t, 0, _n);
        System.arraycopy(r, 0, _r, 0, _n);
        for (int i = 0; i < _n; i++) {
            _rt[i] = _r[i] * _t[i]; // We make no check that rt is ascending (i.e. we allow negative forward rates)
            _df[i] = Math.exp(-_rt[i]);
        }

        _offsetTime = 0.0;
        _offsetRT = 0.0;
    }

    protected ISDACompliantCurve(final ISDACompliantCurve from) {
        ArgumentChecker.notNull(from, "null from");
        // Shallow copy
        _n = from._n;
        _t = from._t;
        _r = from._r;
        _rt = from._rt;
        _df = from._df;

        _offsetTime = from._offsetTime;
        _offsetRT = from._offsetRT;
    }

    /**
     * A curve in which the knots are measured (in fractions of a year) from a particular base-date but the curve is 'observed'
     * from a different base-date. As an example<br>
     * Today (the observation point) is 11-Jul-13, but the yield curve is snapped (bootstrapped from money market and swap rates)
     * on 10-Jul-13 - seen from today there is an offset of -1/365 (assuming a day count of ACT/365) that must be applied to use
     * the yield curve today.  <br>
     * In general, a discount curve observed at time $t_1$ can be written as $P(t_1,T)$. Observed from time $t_2$ this is
     * $P(t_2,T) = \frac{P(t_1,T)}{P(t_1,t_2)}$
     * @param timesFromBaseDate times measured from the base date of the curve
     * @param r zero rates
     * @param offsetFromNewBaseDate if this curve is to be used from a new base-date, what is the offset from the curve base
     */
    protected ISDACompliantCurve(final double[] timesFromBaseDate, final double[] r,
            final double offsetFromNewBaseDate) {
        ArgumentChecker.notEmpty(timesFromBaseDate, "t empty");
        ArgumentChecker.notEmpty(r, "r empty");
        _n = timesFromBaseDate.length;
        ArgumentChecker.isTrue(_n == r.length, "r and t different lengths");
        ArgumentChecker.isTrue(timesFromBaseDate[0] >= 0.0, "timesFromBaseDate must be >= 0");

        // TODO allow larger offsets
        ArgumentChecker.isTrue(timesFromBaseDate[0] + offsetFromNewBaseDate >= 0,
                "offsetFromNewBaseDate too negative");
        for (int i = 1; i < _n; i++) {
            ArgumentChecker.isTrue(timesFromBaseDate[i] > timesFromBaseDate[i - 1], "Times must be ascending");
        }

        _t = new double[_n];
        if (offsetFromNewBaseDate == 0.0) {
            System.arraycopy(timesFromBaseDate, 0, _t, 0, _n);
        } else {
            for (int i = 0; i < _n; i++) {
                _t[i] = timesFromBaseDate[i] + offsetFromNewBaseDate;
            }
        }

        _r = new double[_n];
        _rt = new double[_n];
        _df = new double[_n];
        final double r0 = r[0];
        if (offsetFromNewBaseDate == 0.0) {
            System.arraycopy(r, 0, _r, 0, _n);
        } else {
            _r[0] = r0;
            for (int i = 1; i < _n; i++) {
                _r[i] = r[i] + offsetFromNewBaseDate / _t[i] * (r0 - r[i]);
            }
        }

        _offsetTime = offsetFromNewBaseDate;
        _offsetRT = r0 * _offsetTime;

        for (int i = 0; i < _n; i++) {
            _rt[i] = r[i] * timesFromBaseDate[i]; // We make no check that rt is ascending (i.e. we allow negative forward rates)
            _df[i] = Math.exp(-_rt[i] - _offsetRT);
        }
    }

    /**
     * Constructor mainly used for serialization. This takes all the intermediate calculation results to ensure
     * a strict copy of the original. This should not be the main constructor used in the general case.
     */
    @Deprecated
    public ISDACompliantCurve(final double[] t, final double[] r, final double[] rt, final double[] df,
            final double offsetTime, final double offsetRT) {
        _n = t.length;
        _t = t;
        _r = r;
        _rt = rt;
        _df = df;
        _offsetTime = offsetTime;
        _offsetRT = offsetRT;
    }

    protected double[] getKnotTimes() {
        final double[] res = new double[_n];
        System.arraycopy(_t, 0, res, 0, _n);
        return res;
    }

    protected double[] getKnotZeroRates() {
        final double[] res = new double[_n];
        System.arraycopy(_r, 0, res, 0, _n);
        return res;
    }

    /**
     * The discount factor or survival probability
     * @param t Time
     * @return value
     */
    public double getDiscountFactor(final double t) {
        ArgumentChecker.isTrue(t >= 0, "require t >= 0.0");
        if (t == 0.0) {
            return 1.0;
        }
        final int index = Arrays.binarySearch(_t, t);
        if (index >= 0) {
            return _df[index];
        }

        final int insertionPoint = -(1 + index);
        final double rt = getRT(t, insertionPoint);
        return Math.exp(-rt);
    }

    public double getTimeAtIndex(final int index) {
        return _t[index];
    }

    public double getZeroRateAtIndex(final int index) {
        return _r[index];
    }

    public double getRTAtIndex(final int index) {
        return _rt[index];
    }

    /**
     * The zero rate or zero hazard rate
     * @param t Time
     * @return value
     */
    public double getZeroRate(final double t) {
        ArgumentChecker.isTrue(t >= 0, "require t >= 0.0");

        // short-cut doing binary search
        if (t <= _t[0]) {
            return _r[0];
        }
        if (t > _t[_n - 1]) {
            final double rt = getRT(t, _n - 1);
            return rt / t;
        }

        final int index = Arrays.binarySearch(_t, t);
        if (index >= 0) {
            return _r[index];
        }

        final int insertionPoint = -(1 + index);
        final double rt = getRT(t, insertionPoint);
        return rt / t;
    }

    /**
     * Get the zero rate multiplied by time - this is the same as the negative log of the discount factor
     * @param t  Time
     * @return value
     */
    public double getRT(final double t) {
        ArgumentChecker.isTrue(t >= 0, "require t >= 0.0");

        // short-cut doing binary search
        if (t <= _t[0]) {
            return _r[0] * t;
        }
        if (t > _t[_n - 1]) {
            return getRT(t, _n - 1); //linear extrapolation 
        }

        final int index = Arrays.binarySearch(_t, t);
        if (index >= 0) {
            return _rt[index] + _offsetRT;
        }

        final int insertionPoint = -(1 + index);
        return getRT(t, insertionPoint);
    }

    private double getRT(final double t, final int insertionPoint) {
        if (insertionPoint == 0) {
            return t * _r[0];
        }
        if (insertionPoint == _n) {
            return getRT(t, insertionPoint - 1); //linear extrapolation 
        }

        final double t1 = _t[insertionPoint - 1];
        final double t2 = _t[insertionPoint];
        final double dt = t2 - t1;
        return ((t2 - t) * _rt[insertionPoint - 1] + (t - t1) * _rt[insertionPoint]) / dt + _offsetRT;
    }

    /**
     * 
     * @return number of knots in curve
     */
    public int getNumberOfKnots() {
        return _n;
    }

    /**
     * get the sensitivity of the interpolated rate at time t to the curve node. Note, since the interpolator is highly local, most
     * of the returned values will be zero, so it maybe more efficient to call getSingleNodeSensitivity
     * @param t The time
     * @return sensitivity to the nodes
     */
    public double[] getNodeSensitivity(final double t) {

        final double[] res = new double[_n];

        // short-cut doing binary search
        if (t <= _t[0]) {
            res[0] = 1.0;
            return res;
        }
        if (t >= _t[_n - 1]) {
            final int insertionPoint = _n - 1;
            final double t1 = _t[insertionPoint - 1];
            final double t2 = _t[insertionPoint];
            final double dt = t2 - t1;
            res[insertionPoint - 1] = t1 * (t2 - t) / dt / t;
            res[insertionPoint] = t2 * (t - t1) / dt / t;
            return res;
        }

        final int index = Arrays.binarySearch(_t, t);
        if (index >= 0) {
            res[index] = 1.0;
            return res;
        }

        final int insertionPoint = -(1 + index);
        final double t1 = _t[insertionPoint - 1];
        final double t2 = _t[insertionPoint];
        final double dt = t2 - t1;
        res[insertionPoint - 1] = t1 * (t2 - t) / dt / t;
        res[insertionPoint] = t2 * (t - t1) / dt / t;
        return res;
    }

    /**
     * get the sensitivity of the interpolated zero rate at time t to the value of the zero rate at a given node (knot).  For a
     * given index, i, this is zero unless $$t_{i-1} < t < t_{i+1}$$ since the interpolation is highly local.
     * @param t The time
     * @param nodeIndex The node index
     * @return sensitivity to a single node
     */
    public double getSingleNodeSensitivity(final double t, final int nodeIndex) {
        ArgumentChecker.isTrue(t >= 0, "require t >= 0.0");
        ArgumentChecker.isTrue(nodeIndex >= 0 && nodeIndex < _n, "index out of range");

        if (t <= _t[0]) {
            return nodeIndex == 0 ? 1.0 : 0.0;
        }
        //    if (t >= _t[_n - 1]) {
        //      
        //      return nodeIndex == _n - 1 ? 1.0 : 0.0;
        //    }

        final int index = Arrays.binarySearch(_t, t);
        if (index >= 0) {
            return nodeIndex == index ? 1.0 : 0.0;
        }

        final int insertionPoint = Math.min(_n - 1, -(1 + index));
        if (nodeIndex != insertionPoint && nodeIndex != insertionPoint - 1) {
            return 0.0;
        }

        final double t1 = _t[insertionPoint - 1];
        final double t2 = _t[insertionPoint];
        final double dt = t2 - t1;
        if (nodeIndex == insertionPoint) {
            return t2 * (t - t1) / dt / t;
        }

        return t1 * (t2 - t) / dt / t;
    }

    /**
     * The sensitivity of the discount factor at some time, t, to the value of the zero rate at a given node (knot). For a
     * given index, i, this is zero unless $$t_{i-1} < t < t_{i+1}$$ since the interpolation is highly local.
     * @param t time value of the discount factor
     * @param nodeIndex node index
     * @return sensitivity of a discount factor to a single node
     */
    public double getSingleNodeDiscountFactorSensitivity(final double t, final int nodeIndex) {
        ArgumentChecker.isTrue(t >= 0, "require t >= 0.0");
        ArgumentChecker.isTrue(nodeIndex >= 0 && nodeIndex < _n, "index out of range");

        if (t == 0.0) {
            return 0.0;
        }
        if (t <= _t[0]) {
            return nodeIndex == 0 ? -t * Math.exp(-t * _r[0]) : 0.0;
        }
        //    if (t >= _t[_n - 1]) {
        //      return nodeIndex == _n - 1 ? -t * Math.exp(-t * _r[_n - 1]) : 0.0;
        //    }

        final int index = Arrays.binarySearch(_t, t);
        if (index >= 0) {
            return nodeIndex == index ? -t * _df[nodeIndex] : 0.0;
        }

        final int insertionPoint = Math.min(_n - 1, -(1 + index));
        if (nodeIndex != insertionPoint && nodeIndex != insertionPoint - 1) {
            return 0.0;
        }

        final double t1 = _t[insertionPoint - 1];
        final double t2 = _t[insertionPoint];
        final double dt = t2 - t1;
        final double rt = ((t2 - t) * _rt[insertionPoint - 1] + (t - t1) * _rt[insertionPoint]) / dt;
        final double p = Math.exp(-rt);
        if (nodeIndex == insertionPoint) {
            return -t2 * (t - t1) * p / dt;
        }

        return -t1 * (t2 - t) * p / dt;
    }

    /**
     * A curve in which the knots are measured (in fractions of a year) from a particular base-date but the curve is 'observed'
     * from a different base-date. As an example<br>
     * Today (the observation point) is 11-Jul-13, but the yield curve is snapped (bootstrapped from money market and swap rates)
     * on 10-Jul-13 - seen from today there is an offset of -1/365 (assuming a day count of ACT/365) that must be applied to use
     * the yield curve today.  <br>
     * In general, a discount curve observed at time $t_1$ can be written as $P(t_1,T)$. Observed from time $t_2$ this is
     * $P(t_2,T) = \frac{P(t_1,T)}{P(t_1,t_2)}$
     * @param offsetFromNewBaseDate if this curve is to be used from a new base-date, what is the offset from the curve base
     * @return a new curve with the offset
     */
    public ISDACompliantCurve withOffset(final double offsetFromNewBaseDate) {
        return new ISDACompliantCurve(_t, _r, offsetFromNewBaseDate);
    }

    /**
     * Update are rates in curve.
     * @param r Set of rates
     * @return a new curve
     */
    public ISDACompliantCurve withRates(final double[] r) {
        return new ISDACompliantCurve(_t, r);
    }

    /**
     * Adjust a rate at a particular index.
     * @param rate The new rate
     * @param index The index of the knot
     * @return a new curve
     */
    public ISDACompliantCurve withRate(final double rate, final int index) {
        ArgumentChecker.isTrue(index >= 0 && index < _n, "index out of range");
        final double[] t = new double[_n];
        final double[] r = new double[_n];
        final double[] rt = new double[_n];
        final double[] df = new double[_n];
        System.arraycopy(_t, 0, t, 0, _n);
        System.arraycopy(_r, 0, r, 0, _n);
        System.arraycopy(_rt, 0, rt, 0, _n);
        System.arraycopy(_df, 0, df, 0, _n);
        r[index] = rate;

        rt[index] = rate * (t[index] - _offsetTime);
        df[index] = Math.exp(-rt[index] - _offsetRT);
        return new ISDACompliantCurve(t, r, rt, df, _offsetTime, _offsetRT);
    }

    /**
     * Adjust a discount factor at a particular index.
     * @param discountFactor The new discount factor
     * @param index The index of the knot
     * @return a new curve
     */
    public ISDACompliantCurve withDiscountFactor(final double discountFactor, final int index) {
        if (_offsetTime != 0) { // TODO implement
            throw new NotImplementedException("Please implement");
        }
        ArgumentChecker.isTrue(index >= 0 && index < _n, "index out of range");
        final double[] t = new double[_n];
        final double[] r = new double[_n];
        final double[] rt = new double[_n];
        final double[] df = new double[_n];
        System.arraycopy(_t, 0, t, 0, _n);
        System.arraycopy(_r, 0, r, 0, _n);
        System.arraycopy(_rt, 0, rt, 0, _n);
        System.arraycopy(_df, 0, df, 0, _n);

        df[index] = discountFactor;
        rt[index] = -Math.log(discountFactor);
        r[index] = rt[index] / t[index];
        return new ISDACompliantCurve(t, r, rt, df, 0, 0);
    }

    public double[] getT() {
        return _t;
    }

    public double[] getR() {
        return _r;
    }

    public double[] getRt() {
        return _rt;
    }

    public double[] getDf() {
        return _df;
    }

    public double getOffsetTime() {
        return _offsetTime;
    }

    public double getOffsetRT() {
        return _offsetRT;
    }

    @Override
    public boolean equals(final Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        final ISDACompliantCurve that = (ISDACompliantCurve) o;

        if (_n != that._n) {
            return false;
        }
        if (Double.compare(that._offsetRT, _offsetRT) != 0) {
            return false;
        }
        if (Double.compare(that._offsetTime, _offsetTime) != 0) {
            return false;
        }
        if (!Arrays.equals(_df, that._df)) {
            return false;
        }
        if (!Arrays.equals(_r, that._r)) {
            return false;
        }
        if (!Arrays.equals(_rt, that._rt)) {
            return false;
        }
        if (!Arrays.equals(_t, that._t)) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = _n;
        result = 31 * result + (_t != null ? Arrays.hashCode(_t) : 0);
        result = 31 * result + (_r != null ? Arrays.hashCode(_r) : 0);
        result = 31 * result + (_rt != null ? Arrays.hashCode(_rt) : 0);
        result = 31 * result + (_df != null ? Arrays.hashCode(_df) : 0);
        temp = Double.doubleToLongBits(_offsetTime);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(_offsetRT);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }
}