Swap.java
package net.finmath.finitedifference.interestrate.products;
import net.finmath.finitedifference.interestrate.models.FiniteDifferenceInterestRateModel;
import net.finmath.time.Schedule;
/**
* Finite-difference valuation of a reduced-scope swap.
*
* <p>
* The swap value is
* </p>
*
* <p>
* <i>
* V_{\mathrm{swap}} = V_{\mathrm{receiver\ leg}} - V_{\mathrm{payer\ leg}}.
* </i>
* </p>
*
* <p>
* In line with {@link SwapLeg}, this class is interpreted as a remaining
* forward-looking swap in the current Markovian PDE setting. It is valued
* directly from discount bonds and forward rates and therefore does not require
* a PDE solver.
* </p>
*
* @author Alessandro Gnoatto
*/
public class Swap implements FiniteDifferenceInterestRateProduct {
/**
* The leg receiver.
*/
private final SwapLeg legReceiver;
/**
* The leg payer.
*/
private final SwapLeg legPayer;
/**
* Creates a swap with value
* {@code legReceiver - legPayer}.
*
* @param legReceiver The receiver leg.
* @param legPayer The payer leg.
*/
public Swap(final SwapLeg legReceiver, final SwapLeg legPayer) {
if (legReceiver == null) {
throw new IllegalArgumentException("legReceiver must not be null.");
}
if (legPayer == null) {
throw new IllegalArgumentException("legPayer must not be null.");
}
this.legReceiver = legReceiver;
this.legPayer = legPayer;
}
/**
* Creates a swap from leg specifications.
*
* @param scheduleReceiveLeg The receiver-leg schedule.
* @param forwardCurveReceiveName The receiver-leg forwarding-curve name.
* @param notionalsReceive The receiver-leg notionals.
* @param spreadsReceive The receiver-leg spreads.
* @param schedulePayLeg The payer-leg schedule.
* @param forwardCurvePayName The payer-leg forwarding-curve name.
* @param notionalsPay The payer-leg notionals.
* @param spreadsPay The payer-leg spreads.
* @param isNotionalExchanged True if notional exchange is included on both
* legs.
*/
public Swap(
final Schedule scheduleReceiveLeg,
final String forwardCurveReceiveName,
final double[] notionalsReceive,
final double[] spreadsReceive,
final Schedule schedulePayLeg,
final String forwardCurvePayName,
final double[] notionalsPay,
final double[] spreadsPay,
final boolean isNotionalExchanged) {
this(
new SwapLeg(
scheduleReceiveLeg,
forwardCurveReceiveName,
notionalsReceive,
spreadsReceive,
isNotionalExchanged
),
new SwapLeg(
schedulePayLeg,
forwardCurvePayName,
notionalsPay,
spreadsPay,
isNotionalExchanged
)
);
}
@Override
public double[] getValue(
final double evaluationTime,
final FiniteDifferenceInterestRateModel model) {
final double[] receiverValues = legReceiver.getValue(evaluationTime, model);
final double[] payerValues = legPayer.getValue(evaluationTime, model);
if (receiverValues.length != payerValues.length) {
throw new IllegalArgumentException("Receiver and payer leg value vectors have different lengths.");
}
final double[] values = new double[receiverValues.length];
for (int i = 0; i < values.length; i++) {
values[i] = receiverValues[i] - payerValues[i];
}
return values;
}
@Override
public double[][] getValues(final FiniteDifferenceInterestRateModel model) {
final double[][] receiverValues = legReceiver.getValues(model);
final double[][] payerValues = legPayer.getValues(model);
if (receiverValues.length != payerValues.length) {
throw new IllegalArgumentException("Receiver and payer leg grids have different spatial lengths.");
}
final double[][] values = new double[receiverValues.length][receiverValues[0].length];
for (int i = 0; i < values.length; i++) {
for (int j = 0; j < values[i].length; j++) {
values[i][j] = receiverValues[i][j] - payerValues[i][j];
}
}
return values;
}
/**
* Returns the swap value at the given evaluation time and state.
*
* @param evaluationTime The evaluation time.
* @param stateVariable The state variable.
* @param model The finite-difference interest-rate model.
* @return The swap value.
*/
public double getValueAt(
final double evaluationTime,
final double stateVariable,
final FiniteDifferenceInterestRateModel model) {
return legReceiver.getValueAt(evaluationTime, stateVariable, model)
- legPayer.getValueAt(evaluationTime, stateVariable, model);
}
/**
* Returns the receiver leg.
*
* @return The receiver leg.
*/
public SwapLeg getLegReceiver() {
return legReceiver;
}
/**
* Returns the payer leg.
*
* @return The payer leg.
*/
public SwapLeg getLegPayer() {
return legPayer;
}
/**
* Returns the forward swap rate corresponding to the given fixed-leg and
* floating-leg schedules under the specified model state.
*
* <p>
* The returned quantity is
* </p>
*
* <p>
* <i>
* S(t,x) = \frac{V_{\mathrm{float}}(t,x)}{A(t,x)},
* </i>
* </p>
*
* <p>
* where {@code A(t,x)} is the swap annuity of the fixed-leg schedule.
* </p>
*
* @param evaluationTime The evaluation time.
* @param fixSchedule The fixed-leg schedule.
* @param floatSchedule The floating-leg schedule.
* @param forwardCurveName The forwarding-curve name of the floating leg.
* @param model The finite-difference interest-rate model.
* @param stateVariables The current state variables.
* @return The forward swap rate.
*/
public static double getForwardSwapRate(
final double evaluationTime,
final Schedule fixSchedule,
final Schedule floatSchedule,
final String forwardCurveName,
final FiniteDifferenceInterestRateModel model,
final double... stateVariables) {
if (stateVariables == null || stateVariables.length != 1) {
throw new IllegalArgumentException("Exactly one state variable is required.");
}
final SwapLeg floatingLeg = new SwapLeg(
floatSchedule,
forwardCurveName,
0.0,
false
);
final double valueFloatLeg = floatingLeg.getValueAt(
evaluationTime,
stateVariables[0],
model
);
final double swapAnnuity = SwapAnnuity.getSwapAnnuity(
evaluationTime,
fixSchedule,
model,
stateVariables[0]
);
if (Math.abs(swapAnnuity) < 1E-15) {
throw new IllegalArgumentException("Swap annuity is numerically zero.");
}
return valueFloatLeg / swapAnnuity;
}
@Override
public String toString() {
return "Swap [legReceiver=" + legReceiver + ", legPayer=" + legPayer + "]";
}
}