LIBORIndex.java
/*
* Created on 15.09.2006
*
* (c) Copyright Christian P. Fries, Germany. Contact: email@christian-fries.de.
*/
package net.finmath.montecarlo.interestrate.products.indices;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set;
import net.finmath.exception.CalculationException;
import net.finmath.marketdata.model.AnalyticModel;
import net.finmath.marketdata.model.curves.ForwardCurve;
import net.finmath.montecarlo.interestrate.TermStructureMonteCarloSimulationModel;
import net.finmath.stochastic.RandomVariable;
import net.finmath.time.FloatingpointDate;
import net.finmath.time.businessdaycalendar.BusinessdayCalendar;
import net.finmath.time.businessdaycalendar.BusinessdayCalendar.DateRollConvention;
/**
* A (floating) forward rate index for a given period start offset (offset from fixing) and period length.
*
* @author Christian Fries
* @version 1.1
*/
public class LIBORIndex extends AbstractIndex {
private static final long serialVersionUID = 1L;
private final String paymentOffsetCode;
private final BusinessdayCalendar paymentBusinessdayCalendar;
private final BusinessdayCalendar.DateRollConvention paymentDateRollConvention;
private final double periodStartOffset;
private final double periodLength;
public LIBORIndex(final String name, final String currency, final String paymentOffsetCode,
final BusinessdayCalendar paymentBusinessdayCalendar, final DateRollConvention paymentDateRollConvention) {
super(name, currency);
this.paymentOffsetCode = paymentOffsetCode;
this.paymentBusinessdayCalendar = paymentBusinessdayCalendar;
this.paymentDateRollConvention = paymentDateRollConvention;
periodStartOffset = 0.0;
periodLength = Double.NaN;
}
/**
* Creates a forward rate index for a given period start offset (offset from fixing) and period length.
*
* @param name The name of an index. Used to map an index on a curve.
* @param periodStartOffset An offset added to the fixing to define the period start.
* @param periodLength The period length
*/
public LIBORIndex(final String name, final double periodStartOffset, final double periodLength) {
super(name, null);
paymentOffsetCode = null;
paymentBusinessdayCalendar = null;
paymentDateRollConvention = null;
this.periodStartOffset = periodStartOffset;
this.periodLength = periodLength;
}
/**
* Creates a forward rate index for a given period start offset (offset from fixing) and period length.
*
* @param periodStartOffset An offset added to the fixing to define the period start.
* @param periodLength The period length
*/
public LIBORIndex(final double periodStartOffset, final double periodLength) {
this(null, periodStartOffset, periodLength);
}
@Override
public RandomVariable getValue(final double evaluationTime, final TermStructureMonteCarloSimulationModel model) throws CalculationException {
// Check if model provides this index
if(getName() != null && model.getModel().getForwardRateCurve().getName() != null) {
// Check if analytic adjustment would be possible
if(!model.getModel().getForwardRateCurve().getName().equals(getName()) && (model.getModel().getAnalyticModel() != null && model.getModel().getAnalyticModel().getForwardCurve(getName()) == null)) {
throw new IllegalArgumentException("No curve for index " + getName() + " found in model.");
}
}
// If evaluationTime < 0 take fixing from curve (note: this is a fall-back, fixing should be provided by product, if possible).
if(evaluationTime < 0) {
return model.getRandomVariableForConstant(model.getModel().getForwardRateCurve().getForward(model.getModel().getAnalyticModel(), evaluationTime+periodStartOffset));
}
/*
* The periodLength may be a given float or (more exact) derived from the rolling convetions.
*/
final double periodLength = getPeriodLength(model, evaluationTime+periodStartOffset);
/*
* Fetch forward rate from model
*/
RandomVariable forwardRate = model.getForwardRate(evaluationTime, evaluationTime+periodStartOffset, evaluationTime+periodStartOffset+periodLength);
if(getName() != null && !model.getModel().getForwardRateCurve().getName().equals(getName())) {
// Perform a multiplicative adjustment on the forward bonds
final AnalyticModel analyticModel = model.getModel().getAnalyticModel();
if(analyticModel == null) {
throw new IllegalArgumentException("Index " + getName() + " does not aggree with model curve " + model.getModel().getForwardRateCurve().getName() + " and requires analytic model for adjustment. The analyticModel is null.");
}
final ForwardCurve indexForwardCurve = analyticModel.getForwardCurve(getName());
final ForwardCurve modelForwardCurve = model.getModel().getForwardRateCurve();
final double adjustment = (1.0 + indexForwardCurve.getForward(analyticModel, evaluationTime+periodStartOffset, periodLength) * periodLength) / (1.0 + modelForwardCurve.getForward(analyticModel, evaluationTime+periodStartOffset, periodLength) * periodLength);
forwardRate = forwardRate.mult(periodLength).add(1.0).mult(adjustment).sub(1.0).div(periodLength);
}
return forwardRate;
}
/**
* Returns the periodStartOffset as an act/365 daycount.
*
* @return the periodStartOffset
*/
public double getPeriodStartOffset() {
return periodStartOffset;
}
public double getPeriodLength(final TermStructureMonteCarloSimulationModel model, final double fixingTime) {
if(paymentOffsetCode != null) {
final LocalDateTime referenceDate = model.getReferenceDate();
final LocalDateTime fixingDate = FloatingpointDate.getDateFromFloatingPointDate(referenceDate, fixingTime);
final LocalDate paymentDate = paymentBusinessdayCalendar.getAdjustedDate(fixingDate.toLocalDate(), paymentOffsetCode, paymentDateRollConvention);
final double paymentTime = FloatingpointDate.getFloatingPointDateFromDate(referenceDate, LocalDateTime.of(paymentDate, fixingDate.toLocalTime()));
return paymentTime - fixingTime;
}
else {
return periodLength;
}
}
/**
* Returns the tenor encoded as an pseudo act/365 daycount fraction.
*
* @return the periodLength The tenor as an act/365 daycount fraction.
*/
public double getPeriodLength() {
return periodLength;
}
@Override
public Set<String> queryUnderlyings() {
final Set<String> underlyingNames = new HashSet<>();
underlyingNames.add(getName());
return underlyingNames;
}
@Override
public String toString() {
return "LIBORIndex [periodStartOffset=" + periodStartOffset
+ ", periodLength=" + periodLength + ", toString()="
+ super.toString() + "]";
}
}