SwapLegWithFundingProvider.java

/*
 * (c) Copyright Christian P. Fries, Germany. Contact: email@christian-fries.de.
 *
 * Created on 28.02.2015
 */

package net.finmath.montecarlo.interestrate.products;

import java.time.LocalDateTime;
import java.time.LocalTime;

import net.finmath.exception.CalculationException;
import net.finmath.montecarlo.interestrate.TermStructureMonteCarloSimulationModel;
import net.finmath.montecarlo.interestrate.models.funding.FundingCapacity;
import net.finmath.montecarlo.interestrate.products.indices.AbstractIndex;
import net.finmath.stochastic.RandomVariable;
import net.finmath.stochastic.Scalar;
import net.finmath.time.FloatingpointDate;
import net.finmath.time.Schedule;

/**
 * @author Christian Fries
 *
 * @version 1.0
 */
public class SwapLegWithFundingProvider extends AbstractTermStructureMonteCarloProduct {

	private final Schedule legSchedule;
	private final double[] notionals;
	private final AbstractIndex index;
	private final double[] spreads;

	private final FundingCapacity fundingCapacity;

	/**
	 * Creates a swap leg. The swap leg is build from elementary components.
	 *
	 * @param legSchedule ScheduleFromPeriods of the leg.
	 * @param notionals An array of notionals for each period in the schedule.
	 * @param index The index.
	 * @param spreads Fixed spreads on the forward or fix rate.
	 * @param fundingCapacity A funding capacity monitor.
	 */
	public SwapLegWithFundingProvider(final Schedule legSchedule, final double[] notionals, final AbstractIndex index, final double[] spreads, FundingCapacity fundingCapacity) {
		super();
		if(legSchedule.getNumberOfPeriods() != notionals.length) {
			throw new IllegalArgumentException("Number of notionals ("+notionals.length+") must match number of periods ("+legSchedule.getNumberOfPeriods()+").");
		}

		this.legSchedule = legSchedule;
		this.notionals = notionals;
		this.index =  index;
		this.spreads = spreads;

		this.fundingCapacity = fundingCapacity;
	}

	@Override
	public RandomVariable getValue(final double evaluationTime, final TermStructureMonteCarloSimulationModel model) throws CalculationException {

		final LocalDateTime referenceDate = LocalDateTime.of(legSchedule.getReferenceDate(), LocalTime.of(0, 0));

		double productToModelTimeOffset = 0;
		try {
			if(referenceDate != null && model.getReferenceDate() != null) {
				productToModelTimeOffset = FloatingpointDate.getFloatingPointDateFromDate(model.getReferenceDate(), referenceDate);
			}
		}
		catch(final UnsupportedOperationException e) {
			// @TODO Models that do not provide a reference date will become disfunctional in future releases.
		}

		RandomVariable values = new Scalar(0.0);

		for(int periodIndex=0; periodIndex<legSchedule.getNumberOfPeriods(); periodIndex++) {

			final net.finmath.time.Period period = legSchedule.getPeriod(periodIndex);

			final double periodStart	= legSchedule.getPeriodStart(periodIndex);
			final double periodEnd	= legSchedule.getPeriodEnd(periodIndex);
			final double fixingDate	= legSchedule.getFixing(periodIndex);
			final double paymentDate	= legSchedule.getPayment(periodIndex);
			final double periodLength	= legSchedule.getPeriodLength(periodIndex);

			/*
			 * We do not count empty periods.
			 * Since empty periods are an indication for a ill-specified
			 * product, it might be reasonable to throw an illegal argument exception instead.
			 */
			if(periodLength == 0) {
				continue;
			}

			final double			notionalAtPeriodStart	= notionals[periodIndex];
			final RandomVariable	numeraire				= model.getNumeraire(productToModelTimeOffset + paymentDate);

			RandomVariable payoff = index.getValue(productToModelTimeOffset + fixingDate, model).add(spreads[periodIndex]).mult(periodLength);
			payoff = payoff.mult(notionalAtPeriodStart);

			final RandomVariable survivalProbility = fundingCapacity.getDefaultFactors(productToModelTimeOffset + periodEnd, payoff).getSurvivalProbability();
			payoff = payoff.mult(survivalProbility);

			payoff = payoff.div(numeraire);
			values = values.add(payoff);
		}

		final RandomVariable	numeraireAtEval			= model.getNumeraire(evaluationTime);
		values = values.mult(numeraireAtEval);

		return values;
	}
}