com.opengamma.analytics.financial.interestrate.ParRateCurveSensitivityCalculator.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.analytics.financial.interestrate.ParRateCurveSensitivityCalculator.java

Source

/**
 * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
 * 
 * Please see distribution for license.
 */
package com.opengamma.analytics.financial.interestrate;

import static com.opengamma.analytics.financial.interestrate.InterestRateCurveSensitivityUtils.addSensitivity;
import static com.opengamma.analytics.financial.interestrate.InterestRateCurveSensitivityUtils.multiplySensitivity;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.Validate;

import com.opengamma.analytics.financial.interestrate.annuity.derivative.Annuity;
import com.opengamma.analytics.financial.interestrate.annuity.derivative.AnnuityCouponFixed;
import com.opengamma.analytics.financial.interestrate.annuity.derivative.AnnuityCouponIborSpread;
import com.opengamma.analytics.financial.interestrate.bond.definition.BondFixedSecurity;
import com.opengamma.analytics.financial.interestrate.cash.derivative.Cash;
import com.opengamma.analytics.financial.interestrate.cash.derivative.DepositZero;
import com.opengamma.analytics.financial.interestrate.cash.method.DepositZeroDiscountingMethod;
import com.opengamma.analytics.financial.interestrate.fra.ForwardRateAgreement;
import com.opengamma.analytics.financial.interestrate.fra.method.ForwardRateAgreementDiscountingMethod;
import com.opengamma.analytics.financial.interestrate.future.derivative.InterestRateFuture;
import com.opengamma.analytics.financial.interestrate.payments.ForexForward;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CapFloorIbor;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponFixed;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponIbor;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponIborSpread;
import com.opengamma.analytics.financial.interestrate.payments.derivative.CouponOIS;
import com.opengamma.analytics.financial.interestrate.payments.derivative.Payment;
import com.opengamma.analytics.financial.interestrate.payments.derivative.PaymentFixed;
import com.opengamma.analytics.financial.interestrate.payments.method.CouponIborDiscountingMethod;
import com.opengamma.analytics.financial.interestrate.payments.method.CouponOISDiscountingMethod;
import com.opengamma.analytics.financial.interestrate.swap.derivative.CrossCurrencySwap;
import com.opengamma.analytics.financial.interestrate.swap.derivative.FixedFloatSwap;
import com.opengamma.analytics.financial.interestrate.swap.derivative.FloatingRateNote;
import com.opengamma.analytics.financial.interestrate.swap.derivative.SwapFixedCoupon;
import com.opengamma.analytics.financial.interestrate.swap.derivative.TenorSwap;
import com.opengamma.analytics.financial.interestrate.swap.method.SwapFixedCouponDiscountingMethod;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldAndDiscountCurve;
import com.opengamma.financial.convention.daycount.DayCount;
import com.opengamma.util.CompareUtils;
import com.opengamma.util.tuple.DoublesPair;

/**
 * For an instrument, this calculates the sensitivity of the par rate (the exact meaning of par rate depends on the instrument - for swaps it is the par swap rate) to points on the yield 
 * curve(s) (i.e. dPar/dR at every point the instrument has sensitivity). The return format is a map with curve names (String) as keys and List of DoublesPair as the values; each list holds 
 * set of time (corresponding to point of the yield curve) and sensitivity pairs (i.e. dPar/dR at that time). 
 * <b>Note:</b> The length of the list is instrument dependent and may have repeated times (with the understanding the sensitivities should be summed).
 */
public final class ParRateCurveSensitivityCalculator
        extends AbstractInstrumentDerivativeVisitor<YieldCurveBundle, Map<String, List<DoublesPair>>> {

    /**
     * The method unique instance.
     */
    private static final ParRateCurveSensitivityCalculator INSTANCE = new ParRateCurveSensitivityCalculator();

    /**
     * Return the unique instance of the class.
     * @return The instance.
     */
    public static ParRateCurveSensitivityCalculator getInstance() {
        return INSTANCE;
    }

    /**
     * Constructor.
     */
    ParRateCurveSensitivityCalculator() {
    }

    /**
     * The methods and calculators.
     */
    private static final PresentValueCalculator PV_CALCULATOR = PresentValueCalculator.getInstance();
    private static final ParRateCalculator PRC_CALCULATOR = ParRateCalculator.getInstance();
    private static final PresentValueCurveSensitivityCalculator PV_SENSITIVITY_CALCULATOR = PresentValueCurveSensitivityCalculator
            .getInstance();
    private static final RateReplacingInterestRateDerivativeVisitor REPLACE_RATE = RateReplacingInterestRateDerivativeVisitor
            .getInstance();
    private static final CouponOISDiscountingMethod METHOD_OIS = CouponOISDiscountingMethod.getInstance();
    private static final CouponIborDiscountingMethod METHOD_IBOR = CouponIborDiscountingMethod.getInstance();
    //  private static final CouponIborGearingDiscountingMethod METHOD_IBOR_GEARING = CouponIborGearingDiscountingMethod.getInstance();
    private static final DepositZeroDiscountingMethod METHOD_DEPOSIT_ZERO = DepositZeroDiscountingMethod
            .getInstance();
    private static final SwapFixedCouponDiscountingMethod METHOD_SWAP = SwapFixedCouponDiscountingMethod
            .getInstance();

    @Override
    public Map<String, List<DoublesPair>> visit(final InstrumentDerivative instrument,
            final YieldCurveBundle curves) {
        Validate.notNull(instrument);
        Validate.notNull(curves);
        return instrument.accept(this, curves);
    }

    @Override
    public Map<String, List<DoublesPair>> visitCash(final Cash cash, final YieldCurveBundle curves) {
        final String curveName = cash.getYieldCurveName();
        final YieldAndDiscountCurve curve = curves.getCurve(curveName);
        final double ta = cash.getStartTime();
        final double tb = cash.getEndTime();
        final double yearFrac = cash.getAccrualFactor();
        final Map<String, List<DoublesPair>> result = new HashMap<String, List<DoublesPair>>();
        final List<DoublesPair> temp = new ArrayList<DoublesPair>();
        if (yearFrac == 0.0) {
            if (!CompareUtils.closeEquals(ta, tb, 1e-16)) {
                throw new IllegalArgumentException(
                        "year fraction is zero, but payment time not equal the trade time");
            }
            temp.add(new DoublesPair(ta, 1.0));
        } else {
            final double ratio = curve.getDiscountFactor(ta) / curve.getDiscountFactor(tb) / yearFrac;
            temp.add(new DoublesPair(ta, -ta * ratio));
            temp.add(new DoublesPair(tb, tb * ratio));
        }
        result.put(curveName, temp);
        return result;
    }

    @Override
    public Map<String, List<DoublesPair>> visitDepositZero(final DepositZero deposit,
            final YieldCurveBundle curves) {
        return METHOD_DEPOSIT_ZERO.parRateCurveSensitivity(deposit, curves).getSensitivities();
    }

    @Override
    public Map<String, List<DoublesPair>> visitForwardRateAgreement(final ForwardRateAgreement fra,
            final YieldCurveBundle curves) {
        final ForwardRateAgreementDiscountingMethod method = ForwardRateAgreementDiscountingMethod.getInstance();
        return method.parRateCurveSensitivity(fra, curves).getSensitivities();
    }

    @Override
    public Map<String, List<DoublesPair>> visitInterestRateFuture(final InterestRateFuture future,
            final YieldCurveBundle curves) {
        final String curveName = future.getForwardCurveName();
        final YieldAndDiscountCurve curve = curves.getCurve(curveName);
        final double ta = future.getFixingPeriodStartTime();
        final double tb = future.getFixingPeriodEndTime();
        final double ratio = curve.getDiscountFactor(ta) / curve.getDiscountFactor(tb)
                / future.getFixingPeriodAccrualFactor();
        final DoublesPair s1 = new DoublesPair(ta, -ta * ratio);
        final DoublesPair s2 = new DoublesPair(tb, tb * ratio);
        final List<DoublesPair> temp = new ArrayList<DoublesPair>();
        temp.add(s1);
        temp.add(s2);
        final Map<String, List<DoublesPair>> result = new HashMap<String, List<DoublesPair>>();
        result.put(curveName, temp);
        return result;
    }

    @Override
    public Map<String, List<DoublesPair>> visitFixedCouponSwap(final SwapFixedCoupon<?> swap,
            final YieldCurveBundle curves) {
        final AnnuityCouponFixed unitCouponAnnuity = REPLACE_RATE.visitFixedCouponAnnuity(swap.getFixedLeg(), 1.0);
        final Annuity<?> floatingAnnuity = swap.getSecondLeg();
        final double a = PV_CALCULATOR.visit(unitCouponAnnuity, curves);
        final double b = PV_CALCULATOR.visit(floatingAnnuity, curves);
        final double bOveraSq = b / a / a;
        final Map<String, List<DoublesPair>> senseA = PV_SENSITIVITY_CALCULATOR.visit(unitCouponAnnuity, curves);
        final Map<String, List<DoublesPair>> senseB = PV_SENSITIVITY_CALCULATOR.visit(floatingAnnuity, curves);

        return addSensitivity(multiplySensitivity(senseA, bOveraSq), multiplySensitivity(senseB, -1 / a));
    }

    /**
     * Computes the sensitivity to the curve of swap convention-modified par rate for a fixed coupon swap with a PVBP externally provided.
     * <P>Reference: Swaption pricing - v 1.3, OpenGamma Quantitative Research, June 2012.
     * @param swap The swap.
     * @param dayCount The day count convention to modify the swap rate.
     * @param curves The curves.
     * @return The modified rate.
     */
    public Map<String, List<DoublesPair>> visitFixedCouponSwap(final SwapFixedCoupon<?> swap,
            final DayCount dayCount, final YieldCurveBundle curves) {
        final double pvSecond = PV_CALCULATOR.visit(swap.getSecondLeg(), curves)
                * Math.signum(swap.getSecondLeg().getNthPayment(0).getNotional());
        final double pvbp = METHOD_SWAP.presentValueBasisPoint(swap, dayCount, curves);
        final InterestRateCurveSensitivity pvbpDr = METHOD_SWAP.presentValueBasisPointCurveSensitivity(swap,
                dayCount, curves);
        final InterestRateCurveSensitivity pvSecondDr = new InterestRateCurveSensitivity(
                PV_SENSITIVITY_CALCULATOR.visit(swap.getSecondLeg(), curves))
                        .multiply(Math.signum(swap.getSecondLeg().getNthPayment(0).getNotional()));
        InterestRateCurveSensitivity result = pvSecondDr.multiply(1.0 / pvbp)
                .plus(pvbpDr.multiply(-pvSecond / (pvbp * pvbp)));
        return result.getSensitivities();
    }

    @Override
    public Map<String, List<DoublesPair>> visitCrossCurrencySwap(final CrossCurrencySwap ccs,
            final YieldCurveBundle curves) {
        //wipe any spreads from either FRN
        FloatingRateNote dFRN = REPLACE_RATE.visitFloatingRateNote(ccs.getDomesticLeg(), 0.0);
        FloatingRateNote fFRN = REPLACE_RATE.visitFloatingRateNote(ccs.getForeignLeg(), 0.0);

        AnnuityCouponFixed fAnnuity = fFRN.getFloatingLeg().withUnitCoupons();

        double dPV = PV_CALCULATOR.visit(dFRN, curves);
        double fPV = PV_CALCULATOR.visit(fFRN, curves); //this is in foreign currency
        double fAnnuityPV = PV_CALCULATOR.visit(fAnnuity, curves); //this is in foreign currency

        Map<String, List<DoublesPair>> dPVSense = PV_SENSITIVITY_CALCULATOR.visit(dFRN, curves);
        Map<String, List<DoublesPair>> fPVSense = PV_SENSITIVITY_CALCULATOR.visit(fFRN, curves);
        Map<String, List<DoublesPair>> fAnnuitySense = PV_SENSITIVITY_CALCULATOR.visit(fAnnuity, curves);

        double fx = ccs.getSpotFX(); //TODO remove having CCS holding spot FX rate 

        double temp = -(dPV - fx * fPV) / fx / fAnnuityPV / fAnnuityPV;
        return addSensitivity(multiplySensitivity(dPVSense, 1. / fx / fAnnuityPV),
                multiplySensitivity(fPVSense, -1. / fAnnuityPV), multiplySensitivity(fAnnuitySense, temp));
    }

    @Override
    public Map<String, List<DoublesPair>> visitForexForward(final ForexForward fx, final YieldCurveBundle curves) {

        double fwdFX = PRC_CALCULATOR.visit(fx, curves);
        double t = fx.getPaymentTime();
        List<DoublesPair> temp = new ArrayList<DoublesPair>();
        temp.add(new DoublesPair(t, t * fwdFX));
        final Map<String, List<DoublesPair>> senseD = new HashMap<String, List<DoublesPair>>();
        senseD.put(fx.getPaymentCurrency1().getFundingCurveName(), temp);
        temp = new ArrayList<DoublesPair>();
        temp.add(new DoublesPair(t, -t * fwdFX));
        final Map<String, List<DoublesPair>> senseF = new HashMap<String, List<DoublesPair>>();
        senseF.put(fx.getPaymentCurrency2().getFundingCurveName(), temp);

        return addSensitivity(senseD, senseF);
    }

    /**
     * The assumption is that spread is received (i.e. the spread, if any, is on the received leg only)
     * If the spread is paid (i.e. on the pay leg), swap the legs around and take the negative of the returned value.
     * @param swap 
     * @param curves 
     * @return  The spread on the receive leg of a Tenor swap 
     */
    @Override
    public Map<String, List<DoublesPair>> visitTenorSwap(final TenorSwap<? extends Payment> swap,
            final YieldCurveBundle curves) {
        final AnnuityCouponIborSpread payLeg = ((AnnuityCouponIborSpread) swap.getFirstLeg()).withZeroSpread();
        final AnnuityCouponIborSpread receiveLeg = ((AnnuityCouponIborSpread) swap.getSecondLeg()).withZeroSpread();
        final AnnuityCouponFixed spreadLeg = receiveLeg.withUnitCoupons();

        final double a = PV_CALCULATOR.visit(receiveLeg, curves);
        final double b = PV_CALCULATOR.visit(payLeg, curves);
        final double c = PV_CALCULATOR.visit(spreadLeg, curves);

        final Map<String, List<DoublesPair>> senseA = PV_SENSITIVITY_CALCULATOR.visit(receiveLeg, curves);
        final Map<String, List<DoublesPair>> senseB = PV_SENSITIVITY_CALCULATOR.visit(payLeg, curves);
        final Map<String, List<DoublesPair>> senseC = PV_SENSITIVITY_CALCULATOR.visit(spreadLeg, curves);
        final Map<String, List<DoublesPair>> result = new HashMap<String, List<DoublesPair>>();

        final double factor = (b + a) / c / c;

        for (final String name : curves.getAllNames()) {
            boolean flag = false;
            final List<DoublesPair> temp = new ArrayList<DoublesPair>();
            if (senseA.containsKey(name)) {
                flag = true;
                for (final DoublesPair pair : senseA.get(name)) {
                    temp.add(new DoublesPair(pair.getFirst(), -pair.getSecond() / c));
                }
            }
            if (senseB.containsKey(name)) {
                flag = true;
                for (final DoublesPair pair : senseB.get(name)) {
                    temp.add(new DoublesPair(pair.getFirst(), -pair.getSecond() / c));
                }
            }
            if (senseC.containsKey(name)) {
                flag = true;
                for (final DoublesPair pair : senseC.get(name)) {
                    temp.add(new DoublesPair(pair.getFirst(), factor * pair.getSecond()));
                }
            }
            if (flag) {
                result.put(name, temp);
            }
        }
        return result;
    }

    @Override
    public Map<String, List<DoublesPair>> visitCouponIbor(final CouponIbor payment, final YieldCurveBundle data) {
        return METHOD_IBOR.parRateCurveSensitivity(payment, data).getSensitivities();
    }

    @Override
    public Map<String, List<DoublesPair>> visitCouponIborSpread(final CouponIborSpread payment,
            final YieldCurveBundle data) {
        final String curveName = payment.getForwardCurveName();
        final YieldAndDiscountCurve curve = data.getCurve(curveName);
        //    final double ta = payment.getFixingTime();
        final double ta = payment.getFixingPeriodStartTime();
        final double tb = payment.getFixingPeriodEndTime();
        final double delta = payment.getFixingYearFraction();
        final double ratio = curve.getDiscountFactor(ta) / curve.getDiscountFactor(tb) / delta;
        final DoublesPair s1 = new DoublesPair(ta, -ta * ratio);
        final DoublesPair s2 = new DoublesPair(tb, tb * ratio);
        final List<DoublesPair> temp = new ArrayList<DoublesPair>();
        temp.add(s1);
        temp.add(s2);
        final Map<String, List<DoublesPair>> result = new HashMap<String, List<DoublesPair>>();
        result.put(curveName, temp);
        return result;
    }

    @Override
    public Map<String, List<DoublesPair>> visitCouponOIS(final CouponOIS payment, final YieldCurveBundle data) {
        return METHOD_OIS.parRateCurveSensitivity(payment, data).getSensitivities();
    }

    @Override
    public Map<String, List<DoublesPair>> visitCapFloorIbor(final CapFloorIbor payment,
            final YieldCurveBundle data) {
        return visitCouponIborSpread(payment.toCoupon(), data);
    }

    @Override
    public Map<String, List<DoublesPair>> visitFixedFloatSwap(final FixedFloatSwap swap,
            final YieldCurveBundle data) {
        return visitFixedCouponSwap(swap, data);
    }

    @Override
    public Map<String, List<DoublesPair>> visitBondFixedSecurity(final BondFixedSecurity bond,
            final YieldCurveBundle curves) {
        final Annuity<CouponFixed> coupons = bond.getCoupon();
        final int n = coupons.getNumberOfPayments();
        final CouponFixed[] unitCoupons = new CouponFixed[n];
        for (int i = 0; i < n; i++) {
            unitCoupons[i] = coupons.getNthPayment(i).withUnitCoupon();
        }
        final Annuity<CouponFixed> unitCouponAnnuity = new Annuity<CouponFixed>(unitCoupons);
        final double a = PV_CALCULATOR.visit(unitCouponAnnuity, curves);
        final Map<String, List<DoublesPair>> senseA = PV_SENSITIVITY_CALCULATOR.visit(unitCouponAnnuity, curves);
        final Map<String, List<DoublesPair>> result = new HashMap<String, List<DoublesPair>>();
        final PaymentFixed principlePaymemt = bond.getNominal().getNthPayment(0);
        final double df = PV_CALCULATOR.visit(principlePaymemt, curves);
        final double factor = -(1 - df) / a / a;
        for (final String name : curves.getAllNames()) {
            if (senseA.containsKey(name)) {
                final List<DoublesPair> temp = new ArrayList<DoublesPair>();
                final List<DoublesPair> list = senseA.get(name);
                final int m = list.size();
                for (int i = 0; i < (m - 1); i++) {
                    final DoublesPair pair = list.get(i);
                    temp.add(new DoublesPair(pair.getFirst(), factor * pair.getSecond()));
                }
                final DoublesPair pair = list.get(m - 1);
                temp.add(new DoublesPair(pair.getFirst(),
                        principlePaymemt.getPaymentTime() * df / a + factor * pair.getSecond()));
                result.put(name, temp);
            }
        }
        return result;
    }
}