All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.finmath.marketdata.model.curves.SeasonalCurve Maven / Gradle / Ivy

/*
 * (c) Copyright Christian P. Fries, Germany. Contact: [email protected].
 *
 * Created on 07.03.2015
 */

package net.finmath.marketdata.model.curves;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import net.finmath.marketdata.model.AnalyticModel;
import net.finmath.marketdata.model.curves.CurveInterpolation.ExtrapolationMethod;
import net.finmath.marketdata.model.curves.CurveInterpolation.InterpolationEntity;
import net.finmath.marketdata.model.curves.CurveInterpolation.InterpolationMethod;
import net.finmath.time.daycount.DayCountConvention;
import net.finmath.time.daycount.DayCountConvention_ACT_365;

/**
 * The curve returns a value depending on the month of the time argument, that is,
 * a call getValue(model, time) will map time to a 30/360 value using
 * the day and month only and delegate the call to a given base curve.
 *
 * The value returned then is baseCurve.getValue(model, season)
 * where
 * season = (month-1) / 12.0 + (day-1) / (double)numberOfDays / 12.0;
 *
 * The base curve has to be constructed according to this time convention (e.g.,
 * as a piecewise constant curve with values at i / 12 for i=1,...,12 using
 * {@link CurveInterpolation.InterpolationMethod} with PIECEWISE_CONSTANT_RIGHTPOINT.
 *
 * @author Christian Fries
 * @version 1.0
 */
public class SeasonalCurve extends AbstractCurve implements Curve {

	private static final long serialVersionUID = 4021745191829488593L;

	private Curve baseCurve;

	/**
	 * A builder (following the builder pattern) for SeasonalCurve objects.
	 * Allows to successively construct a curve object by adding points to its base points.
	 *
	 * @author Christian Fries
	 */
	public static class Builder extends CurveInterpolation.Builder implements CurveBuilder {

		private SeasonalCurve			curve = null;

		/**
		 * Create a CurveBuilder from a given seasonalCurve.
		 *
		 * @param seasonalCurve The seasonal curve from which to copy the fixed part upon build().
		 * @throws CloneNotSupportedException Thrown, when the base curve could not be cloned.
		 */
		public Builder(final SeasonalCurve seasonalCurve) throws CloneNotSupportedException {
			super((CurveInterpolation)(seasonalCurve.baseCurve));
			curve = seasonalCurve;
		}

		@Override
		public Curve build() throws CloneNotSupportedException {
			final SeasonalCurve buildCurve = curve.clone();
			buildCurve.baseCurve = super.build();
			curve = null;
			return buildCurve;
		}
	}

	/**
	 * Create a monthly seasonality adjustment curve by estimating historic log-returns from monthly index fixings.
	 *
	 * @param name The name of this curve.
	 * @param referenceDate The reference date for this curve (i.e. t=0).
	 * @param indexFixings A Map<Date, Double> of consecutive monthly index fixings.
	 * @param numberOfYearsToAverage The number of years over which monthly log returns should be averaged.
	 */
	public SeasonalCurve(final String name, final LocalDate referenceDate, final Map indexFixings, final int numberOfYearsToAverage) {
		super(name, referenceDate);

		final double[] seasonalAdjustmentCalculated = SeasonalCurve.computeSeasonalAdjustments(referenceDate, indexFixings, numberOfYearsToAverage);

		final double[] seasonTimes = new double[12];
		final double[] seasonValue = new double[12];
		double seasonValueCummulated = 1.0;
		for(int j=0; j<12; j++) {
			seasonValueCummulated *= Math.exp(seasonalAdjustmentCalculated[j]/12.0);
			seasonTimes[j] = j/12.0;
			seasonValue[j] = seasonValueCummulated;
		}
		baseCurve = new CurveInterpolation(name + "-seasonal-base", referenceDate, InterpolationMethod.PIECEWISE_CONSTANT_LEFTPOINT, ExtrapolationMethod.CONSTANT, InterpolationEntity.VALUE, seasonTimes, seasonValue);
	}

	/**
	 * @param name The name of this curve.
	 * @param referenceDate The reference date for this curve (i.e. t=0).
	 * @param baseCurve The base curve, i.e., the discount curve used to calculate the seasonal adjustment factors.
	 */
	public SeasonalCurve(final String name, final LocalDate referenceDate, final Curve baseCurve) {
		super(name, referenceDate);
		this.baseCurve = baseCurve;
	}

	@Override
	public double[] getParameter() {
		return baseCurve.getParameter();
	}

	@Override
	public void setParameter(final double[] parameter) {
		baseCurve.setParameter(parameter);
	}

	@Override
	public double getValue(final AnalyticModel model, final double time) {
		final LocalDate calendar = getReferenceDate().plusDays((int) Math.round(time*365));

		final int month = calendar.getMonthValue();				// Note: month = 1,2,3,...,12
		final int day   = calendar.getDayOfMonth(); 				// Note: day = 1,2,3,...,numberOfDays
		final int numberOfDays = calendar.lengthOfMonth();
		final double season = (month-1) / 12.0 + (day-1) / (double)numberOfDays / 12.0;

		return baseCurve.getValue(model, season);
	}

	@Override
	public Curve getCloneForParameter(final double[] value) throws CloneNotSupportedException {
		final SeasonalCurve newCurve = clone();
		newCurve.baseCurve = baseCurve.getCloneForParameter(value);

		return newCurve;
	}

	@Override
	public SeasonalCurve clone() throws CloneNotSupportedException {
		return new SeasonalCurve(this.getName(), this.getReferenceDate(), (Curve) baseCurve.clone());
	}

	@Override
	public Builder getCloneBuilder() throws CloneNotSupportedException {
		return new Builder(this);
	}

	public static double[] computeSeasonalAdjustments(final LocalDate referenceDate, final Map indexFixings, final int numberOfYearsToAverage) {
		final DayCountConvention modelDcc = new DayCountConvention_ACT_365();			// Not needed: remove

		final double[] fixingTimes = new double[indexFixings.size()];								// Not needed: remove
		final double[] realizedCPIValues = new double[indexFixings.size()];
		int i = 0;
		final List fixingDates = new ArrayList<>(indexFixings.keySet());
		Collections.sort(fixingDates);
		for(final LocalDate fixingDate : fixingDates) {
			fixingTimes[i] = modelDcc.getDaycountFraction(referenceDate, fixingDate);
			realizedCPIValues[i] = indexFixings.get(fixingDate).doubleValue();
			i++;
		}

		final LocalDate lastMonth = fixingDates.get(fixingDates.size()-1);

		return computeSeasonalAdjustments(realizedCPIValues, lastMonth.getMonthValue(), numberOfYearsToAverage);
	}

	/**
	 * Computes annualized seasonal adjustments from given monthly realized CPI values.
	 *
	 * @param realizedCPIValues An array of consecutive monthly CPI values (minimum size is 12*numberOfYearsToAverage))
	 * @param lastMonth The index of the last month in the sequence of realizedCPIValues (corresponding to the enums in {@link java.time.Month}).
	 * @param numberOfYearsToAverage The number of years to go back in the array of realizedCPIValues.
	 * @return Array of annualized seasonal adjustments, where [0] corresponds to the adjustment for from December to January.
	 */
	public static double[] computeSeasonalAdjustments(final double[] realizedCPIValues, final int lastMonth, final int numberOfYearsToAverage) {

		/*
		 * Cacluate average log returns
		 */
		final double[] averageLogReturn = new double[12];
		Arrays.fill(averageLogReturn, 0.0);
		for(int arrayIndex = 0; arrayIndex < 12*numberOfYearsToAverage; arrayIndex++){

			final int month = (((((lastMonth-1 - arrayIndex) % 12) + 12) % 12));

			final double logReturn = Math.log(realizedCPIValues[realizedCPIValues.length - 1 - arrayIndex] / realizedCPIValues[realizedCPIValues.length - 2 - arrayIndex]);
			averageLogReturn[month] += logReturn/numberOfYearsToAverage;
		}

		/*
		 * Normalize
		 */
		double sum = 0.0;
		for(int index = 0; index < averageLogReturn.length; index++){
			sum += averageLogReturn[index];
		}
		final double averageSeasonal = sum / averageLogReturn.length;

		final double[] seasonalAdjustments = new double[averageLogReturn.length];
		for(int index = 0; index < seasonalAdjustments.length; index++){
			seasonalAdjustments[index] = averageLogReturn[index] - averageSeasonal;
		}

		// Annualize seasonal adjustments
		for(int index = 0; index < seasonalAdjustments.length; index++){
			seasonalAdjustments[index] = seasonalAdjustments[index] * 12;
		}

		return seasonalAdjustments;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy