/*
* (c) Copyright Christian P. Fries, Germany. All rights reserved. Contact: email@christian-fries.de.
*
* Created on 17.05.2007
*/
package net.finmath.montecarlo.interestrate.products;
import java.util.HashMap;
import java.util.Map;
import net.finmath.functions.AnalyticFormulas;
import net.finmath.marketdata.model.curves.ForwardCurveInterface;
import net.finmath.montecarlo.RandomVariable;
import net.finmath.montecarlo.interestrate.LIBORMarketModel;
import net.finmath.montecarlo.interestrate.LIBORMarketModelInterface;
import net.finmath.montecarlo.interestrate.LIBORModelMonteCarloSimulationInterface;
import net.finmath.stochastic.RandomVariableInterface;
import net.finmath.time.TimeDiscretizationInterface;
/**
* This class implements an analytic swaption valuation formula under
* a LIBOR market model. The algorithm implemented here is
* taken from ISBN 0470047224.
*
* The approximation assumes that the forward rates (LIBOR) follow a
* log normal model and that the model provides the integrated
* instantaneous covariance of the log-forward rates.
*
* The getValue method calculates the approximated integrated instantaneous variance of the swap rate,
* using the approximation d log(S(t))/d log(L(t)) = d log(S(0))/d log(L(0)).
*
* The valuation can be performed in terms of value or implied
* volatility.
*
*
* @author Christian Fries
*/
public class SwaptionSingleCurveAnalyticApproximation extends AbstractLIBORMonteCarloProduct {
public enum ValueUnit {
VALUE,
INTEGRATEDVARIANCE,
VOLATILITY
}
private final double swaprate;
private final double[] swapTenor; // Vector of swap tenor (period start and end dates). Start of first period is the option maturity.
private final ValueUnit valueUnit;
/**
* Create an analytic swaption approximation product for
* log normal forward rate model.
*
* Note: It is implicitly assumed that swapTenor.getTime(0) is the exercise date (no forward starting).
*
* @param swaprate The strike swap rate of the swaption.
* @param swapTenor The swap tenor in doubles.
*/
public SwaptionSingleCurveAnalyticApproximation(double swaprate, TimeDiscretizationInterface swapTenor) {
this(swaprate, swapTenor.getAsDoubleArray(), ValueUnit.VALUE);
}
/**
* Create an analytic swaption approximation product for
* log normal forward rate model.
*
* Note: It is implicitly assumed that swapTenor[0] is the exercise date (no forward starting).
*
* @param swaprate The strike swap rate of the swaption.
* @param swapTenor The swap tenor in doubles.
* @param valueUnit The unit of the quantity returned by the getValues method.
*/
public SwaptionSingleCurveAnalyticApproximation(double swaprate, double[] swapTenor, ValueUnit valueUnit) {
super();
this.swaprate = swaprate;
this.swapTenor = swapTenor;
this.valueUnit = valueUnit;
}
@Override
public RandomVariableInterface getValue(double evaluationTime, LIBORModelMonteCarloSimulationInterface model) {
return getValues(evaluationTime, model.getModel());
}
/**
* Calculates the approximated integrated instantaneous variance of the swap rate,
* using the approximation d log(S(t))/d log(L(t)) = d log(S(0))/d log(L(0)).
*
* @param evaluationTime Time at which the product is evaluated.
* @param model A model implementing the LIBORModelMonteCarloSimulationInterface
* @return Depending on the value of value unit, the method returns either
* the approximated integrated instantaneous variance of the swap rate (ValueUnit.INTEGRATEDVARIANCE)
* or the value using the Black formula (ValueUnit.VALUE).
* @TODO make initial values an arg and use evaluation time.
*/
public RandomVariableInterface getValues(double evaluationTime, LIBORMarketModelInterface model) {
if(evaluationTime > 0) throw new RuntimeException("Forward start evaluation currently not supported.");
double swapStart = swapTenor[0];
double swapEnd = swapTenor[swapTenor.length-1];
int swapStartIndex = model.getLiborPeriodIndex(swapStart);
int swapEndIndex = model.getLiborPeriodIndex(swapEnd);
int optionMaturityIndex = model.getCovarianceModel().getTimeDiscretization().getTimeIndex(swapStart)-1;
Map logSwaprateDerivative = getLogSwaprateDerivative(model.getLiborPeriodDiscretization(), model.getForwardRateCurve(), swapTenor);
double[] swapCovarianceWeights = logSwaprateDerivative.get("values");
double[] discountFactors = logSwaprateDerivative.get("discountFactors");
double[] swapAnnuities = logSwaprateDerivative.get("swapAnnuities");
// Get the integrated libor covariance from the model
double[][] integratedLIBORCovariance = model.getIntegratedLIBORCovariance()[optionMaturityIndex];
// Calculate integrated swap rate covariance
double integratedSwapRateVariance = 0.0;
for(int componentIndex1 = swapStartIndex; componentIndex1 < swapEndIndex; componentIndex1++) {
// Sum the libor cross terms (use symmetry)
for(int componentIndex2 = componentIndex1+1; componentIndex2 < swapEndIndex; componentIndex2++) {
integratedSwapRateVariance += 2.0 * swapCovarianceWeights[componentIndex1-swapStartIndex] * swapCovarianceWeights[componentIndex2-swapStartIndex] * integratedLIBORCovariance[componentIndex1][componentIndex2];
}
// Add diagonal term (libor variance term)
integratedSwapRateVariance += swapCovarianceWeights[componentIndex1-swapStartIndex] * swapCovarianceWeights[componentIndex1-swapStartIndex] * integratedLIBORCovariance[componentIndex1][componentIndex1];
}
// Return integratedSwapRateVariance if requested
if(valueUnit == ValueUnit.INTEGRATEDVARIANCE) return new RandomVariable(evaluationTime, integratedSwapRateVariance);
double volatility = Math.sqrt(integratedSwapRateVariance / swapStart);
// Return integratedSwapRateVariance if requested
if(valueUnit == ValueUnit.VOLATILITY) return new RandomVariable(evaluationTime, volatility);
// Use black formula for swaption to calculate the price
double swapAnnuity = swapAnnuities[0];
double parSwaprate = (discountFactors[0] - discountFactors[swapEndIndex-swapStartIndex]) / swapAnnuity;
double optionMaturity = swapStart;
double valueSwaption = AnalyticFormulas.blackModelSwaptionValue(parSwaprate, volatility, optionMaturity, swaprate, swapAnnuity);
return new RandomVariable(evaluationTime, valueSwaption);
}
/**
* This function calculate the partial derivative d log(S) / d log(Lk) for
* a given swap rate with respect to a vector of forward rates (on a given forward rate tenor).
*
* It also returns some useful other quantities like the corresponding discout factors and swap annuities.
*
* @param liborPeriodDiscretization The libor period discretization.
* @param forwardCurveInterface The forward curve.
* @param swapTenor The swap tenor.
* @return A map containing the partial derivatives (key "value"), the discount factors (key "disocuntFactors") and the annuities (key "annuities") as vectors of double[] (indexed by forward rate tenor index starting at swap start)
*/
static public Map getLogSwaprateDerivative(TimeDiscretizationInterface liborPeriodDiscretization, ForwardCurveInterface forwardCurveInterface, double[] swapTenor) {
double swapStart = swapTenor[0];
double swapEnd = swapTenor[swapTenor.length-1];
// Get the indices of the swap start and end on the forward rate tenor
int swapStartIndex = liborPeriodDiscretization.getTimeIndex(swapStart);
int swapEndIndex = liborPeriodDiscretization.getTimeIndex(swapEnd);
// Precalculate forward rates and discount factors. Note: the swap contains swapEndIndex-swapStartIndex forward rates
double[] forwardRates = new double[swapEndIndex-swapStartIndex+1];
double[] discountFactors = new double[swapEndIndex-swapStartIndex+1];
// Calculate discount factor at swap start
discountFactors[0] = 1.0;
for(int liborPeriodIndex = 0; liborPeriodIndex < swapStartIndex; liborPeriodIndex++) {
double libor = forwardCurveInterface.getForward(null, liborPeriodDiscretization.getTime(liborPeriodIndex));
double liborPeriodLength = liborPeriodDiscretization.getTimeStep(liborPeriodIndex);
discountFactors[0] /= 1 + libor * liborPeriodLength;
}
// Calculate discount factors for swap period ends (used for swap annuity)
for(int liborPeriodIndex = swapStartIndex; liborPeriodIndex < swapEndIndex; liborPeriodIndex++) {
double libor = forwardCurveInterface.getForward(null, liborPeriodDiscretization.getTime(liborPeriodIndex));
double liborPeriodLength = liborPeriodDiscretization.getTimeStep(liborPeriodIndex);
forwardRates[liborPeriodIndex-swapStartIndex] = libor;
discountFactors[liborPeriodIndex-swapStartIndex+1] = discountFactors[liborPeriodIndex-swapStartIndex] / (1 + libor * liborPeriodLength);
}
// Precalculate swap annuities
double[] swapAnnuities = new double[swapTenor.length-1];
double swapAnnuity = 0.0;
for(int swapPeriodIndex = swapTenor.length-2; swapPeriodIndex >= 0; swapPeriodIndex--) {
int periodEndIndex = liborPeriodDiscretization.getTimeIndex(swapTenor[swapPeriodIndex+1]);
swapAnnuity += discountFactors[periodEndIndex-swapStartIndex] * (swapTenor[swapPeriodIndex+1]-swapTenor[swapPeriodIndex]);
swapAnnuities[swapPeriodIndex] = swapAnnuity;
}
// Precalculate weights: The formula is take from ISBN 0470047224
double longForwardRate = discountFactors[swapEndIndex-swapStartIndex] / ( discountFactors[0] - discountFactors[swapEndIndex-swapStartIndex]);
double[] swapCovarianceWeights = new double[swapEndIndex-swapStartIndex];
int swapPeriodIndex = 0;
for(int liborPeriodIndex = swapStartIndex; liborPeriodIndex < swapEndIndex; liborPeriodIndex++) {
if(liborPeriodDiscretization.getTime(liborPeriodIndex) >= swapTenor[swapPeriodIndex+1]) swapPeriodIndex++;
swapCovarianceWeights[liborPeriodIndex-swapStartIndex] = (longForwardRate + swapAnnuities[swapPeriodIndex] / swapAnnuity) * (1.0 - discountFactors[liborPeriodIndex-swapStartIndex+1] / discountFactors[liborPeriodIndex-swapStartIndex]);
}
// Return results
Map results = new HashMap();
results.put("values", swapCovarianceWeights);
results.put("discountFactors", discountFactors);
results.put("swapAnnuities", swapAnnuities);
return results;
}
static public double[][][] getIntegratedLIBORCovariance(LIBORMarketModel model) {
return model.getIntegratedLIBORCovariance();
}
}
Ads help maintain this website.