/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.analytics.financial.model.interestrate.curve; import com.opengamma.analytics.math.curve.DoublesCurve; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.time.DateUtils; /** * Wraps a normal {@link DiscountCurve} and pre-calculates all future points * over a number of years, rendering all calls to obtain discount factors * to an array lookup. If {@link #preCalculateDiscountFactors(int)} is not called, * this class behaves precisely as a normal {@link DiscountCurve}. * <p/> * This is <em>not</em> a general purpose class, and is only appropriate when * the following constraints are met: * <ul> * <li>Curve data changes on a slow period (on the order of hours or days);</li> * <li>Forward points are only requested on a daily boundary (e.g. at midnight, * rather than for discrete times during a day);</li> * <li>The underlying curve is using a sufficiently slow interpolator that the * pre-computation is appropriate</li> * <li>Sufficiently large portfolios or numbers of calculations will happen * with the same curve object to warrant the pre-computation.</li> * <li>No extrapolation is required.</li> * </ul> * <p/> * Given these caveats, this class should only be used in cases where performance testing * and constraints on the problem domain warrant developers using this class. * <p/> * For more information, see <a href="http://jira.opengamma.com/browse/PLAT-4688">PLAT-4688</a>. */ public class DayPeriodPreCalculatedDiscountCurve extends DiscountCurve { /** Array containing the pre-calculated discount factors */ private double[] _preCalculatedDiscountFactors; /** The days in a year - used to convert from a fraction of a year to a whole number of days */ private final double _daysPerYear; /** * Create a curve capable of pre interpolation. * Assumes 365.25 days per year. * * @param name The discount curve name. * @param discountFactorCurve The underlying curve. */ public DayPeriodPreCalculatedDiscountCurve(final String name, final DoublesCurve discountFactorCurve) { this(name, discountFactorCurve, DateUtils.DAYS_PER_YEAR); } /** * @param name The discount curve name. * @param discountFactorCurve The underlying curve. * @param daysPerYear the days per year, used to convert a time as a double to a whole number of days. */ public DayPeriodPreCalculatedDiscountCurve(final String name, final DoublesCurve discountFactorCurve, final double daysPerYear) { super(name, discountFactorCurve); _daysPerYear = daysPerYear; } /** * Pre-calculate all discount factors for every single discrete day * over the given number of years. * @param numYears the number of years to pre-calculate. */ public void preCalculateDiscountFactors(final int numYears) { ArgumentChecker.isTrue(numYears >= 1, "numYears must be more than 1"); // Because of leap year and other calendar issues, we can't actually // accurately pre-calculate the number of forward days over that number // of years. Therefore, we overallocate the array and go beyond the end // of the year range slightly. _preCalculatedDiscountFactors = new double[numYears * 366]; int numDays = 0; while (numDays < _preCalculatedDiscountFactors.length) { final double xValue = (numDays) / _daysPerYear; final double yValue = super.getDiscountFactor(xValue); _preCalculatedDiscountFactors[numDays] = yValue; numDays++; } } @Override public double getDiscountFactor(final double t) { if (_preCalculatedDiscountFactors == null) { return super.getDiscountFactor(t); } final long nDaysLong = Math.round(t * _daysPerYear); if (nDaysLong > Integer.MAX_VALUE) { return super.getDiscountFactor(t); } final int nDays = (int) nDaysLong; if (nDays > _preCalculatedDiscountFactors.length) { return super.getDiscountFactor(t); } return _preCalculatedDiscountFactors[nDays]; } /** * Returns true if the discount factors are pre-calculated. * @return True if the discount factors are pre-calculated */ public boolean isPreCalculated() { return _preCalculatedDiscountFactors != null; } /** * Returns true if the discount factors are pre-calculated for the number of days. * @param nDaysForward The number of days * @return True if the discount factors are pre-calculated for the number of days */ public boolean isPreCalculated(final int nDaysForward) { return (_preCalculatedDiscountFactors != null) && (_preCalculatedDiscountFactors.length >= nDaysForward); } }