SwapLeg.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 java.util.ArrayList;
import java.util.Collection;
import net.finmath.exception.CalculationException;
import net.finmath.montecarlo.interestrate.TermStructureMonteCarloSimulationModel;
import net.finmath.montecarlo.interestrate.products.components.AbstractProductComponent;
import net.finmath.montecarlo.interestrate.products.components.AccruingNotional;
import net.finmath.montecarlo.interestrate.products.components.Notional;
import net.finmath.montecarlo.interestrate.products.components.Period;
import net.finmath.montecarlo.interestrate.products.components.ProductCollection;
import net.finmath.montecarlo.interestrate.products.indices.AbstractIndex;
import net.finmath.montecarlo.interestrate.products.indices.FixedCoupon;
import net.finmath.montecarlo.interestrate.products.indices.LinearCombinationIndex;
import net.finmath.stochastic.RandomVariable;
import net.finmath.time.Schedule;
/**
* @author Christian Fries
*
* @version 1.0
*/
public class SwapLeg extends AbstractTermStructureMonteCarloProduct {
private final ProductCollection components;
/**
* Creates a swap leg. The swap leg is build from elementary components.
*
* @param legSchedule ScheduleFromPeriods of the leg.
* @param notional The notional.
* @param index The index.
* @param spread Fixed spread on the forward or fix rate.
* @param couponFlow If true, the coupon is payed. If false, the coupon is not payed, but may still be part of an accruing notional, see <code>isNotionalAccruing</code>.
* @param isNotionalExchanged If true, the leg will pay notional at the beginning of the swap and receive notional at the end of the swap.
* @param isNotionalAccruing If true, the notional is accruing, that is, the notional of a period is given by the notional of the previous period, accrued with the coupon of the previous period.
*/
public SwapLeg(final Schedule legSchedule, Notional notional, final AbstractIndex index, final double spread, final boolean couponFlow, final boolean isNotionalExchanged, final boolean isNotionalAccruing) {
super();
final LocalDateTime referenceDate = legSchedule.getReferenceDate() != null ? legSchedule.getReferenceDate().atStartOfDay() : null;
/*
* Create components.
*
* The interesting part here is, that the creation of the components implicitly
* constitutes the (traditional) pricing algorithms (e.g., loop over all periods).
* Hence, the definition of the product is the definition of the pricing algorithm.
*/
final Collection<AbstractProductComponent> periods = new ArrayList<>();
for(int periodIndex=0; periodIndex<legSchedule.getNumberOfPeriods(); 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;
}
AbstractIndex coupon;
if(index != null) {
if(spread != 0) {
coupon = new LinearCombinationIndex(1, index, 1, new FixedCoupon(spread));
} else {
coupon = index;
}
}
else {
coupon = new FixedCoupon(spread);
}
final Period period = new Period(referenceDate, fixingDate, paymentDate, fixingDate, paymentDate, notional, coupon, periodLength, couponFlow, isNotionalExchanged, false, false);
periods.add(period);
if(isNotionalAccruing) {
notional = new AccruingNotional(notional, period);
}
}
components = new ProductCollection(periods);
}
/**
* 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 couponFlow If true, the coupon is payed. If false, the coupon is not payed, but may still be part of an accruing notional, see <code>isNotionalAccruing</code>.
* @param isNotionalExchanged If true, the leg will pay notional at the beginning of the swap and receive notional at the end of the swap.
*/
public SwapLeg(final Schedule legSchedule, final Notional[] notionals, final AbstractIndex index, final double[] spreads, final boolean couponFlow, final boolean isNotionalExchanged) {
super();
if(legSchedule.getNumberOfPeriods() != notionals.length) {
throw new IllegalArgumentException("Number of notionals ("+notionals.length+") must match number of periods ("+legSchedule.getNumberOfPeriods()+").");
}
/*
* Create components.
*
* The interesting part here is, that the creation of the components implicitly
* constitutes the (traditional) pricing algorithms (e.g., loop over all periods).
* Hence, the definition of the product is the definition of the pricing algorithm.
*/
final Collection<AbstractProductComponent> periods = new ArrayList<>();
for(int periodIndex=0; periodIndex<legSchedule.getNumberOfPeriods(); periodIndex++) {
final LocalDateTime referenceDate = LocalDateTime.of(legSchedule.getReferenceDate(), LocalTime.of(0, 0));
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;
}
AbstractIndex coupon;
if(index != null) {
if(spreads[periodIndex] != 0) {
coupon = new LinearCombinationIndex(1, index, 1, new FixedCoupon(spreads[periodIndex]));
} else {
coupon = index;
}
}
else {
coupon = new FixedCoupon(spreads[periodIndex]);
}
final Period period = new Period(referenceDate, periodStart, periodEnd, fixingDate, paymentDate, notionals[periodIndex], coupon,
periodLength, couponFlow, isNotionalExchanged, false /* payer */, false /* isExcludeAccruedInterest */);
periods.add(period);
}
components = new ProductCollection(periods);
}
/**
* Creates a swap leg. The swap leg is build from elementary components
*
* @param legSchedule ScheduleFromPeriods of the leg.
* @param notional The notional.
* @param index The index.
* @param spread Fixed spread on the forward or fix rate.
* @param isNotionalExchanged If true, the leg will pay notional at the beginning of the swap and receive notional at the end of the swap.
*/
public SwapLeg(final Schedule legSchedule, final Notional notional, final AbstractIndex index, final double spread, final boolean isNotionalExchanged) {
this(legSchedule, notional, index, spread, true, isNotionalExchanged, false);
}
@Override
public RandomVariable getValue(final double evaluationTime, final TermStructureMonteCarloSimulationModel model) throws CalculationException {
return components.getValue(evaluationTime, model);
}
}