private void presentValueSensitivityRawDataParallelSensitivity(SabrParametersSwaptionVolatilities sabrCalibrated, TenorRawOptionData dataRaw) { PointSensitivities points = LEG_PRICER.presentValueSensitivityModelParamsSabr(FLOOR_LEG, MULTICURVE, sabrCalibrated).build(); CurrencyParameterSensitivities sabrParametersSurfaceSensitivities = sabrCalibrated.parameterSensitivity(points); CurrencyParameterSensitivity parallelSensitivitiesSurface = RDSC.parallelSensitivity(sabrParametersSurfaceSensitivities, sabrCalibrated); DoubleArray sensitivityArray = parallelSensitivitiesSurface.Sensitivity; double fdShift = 1.0E-6; int surfacePointIndex = 0; for (int loopexpiry = 0; loopexpiry < EXPIRIES.size(); loopexpiry++) { for (int looptenor = 0; looptenor < TENORS.size(); looptenor++) { Tenor tenor = TENORS.get(looptenor); Pair <DoubleArray, DoubleArray> ds = dataRaw.getData(tenor).availableSmileAtExpiry(EXPIRIES.get(loopexpiry)); if (!ds.First.Empty) { double[] pv = new double[2]; // pv with shift up and down for (int loopsign = 0; loopsign < 2; loopsign++) { TenorRawOptionData dataShifted = SabrSwaptionCalibratorSmileTestUtils.rawDataShiftSmile(TENORS, EXPIRIES, ValueType.SIMPLE_MONEYNESS, MONEYNESS, ValueType.NORMAL_VOLATILITY, DATA_ARRAY_FULL, looptenor, loopexpiry, (2 * loopsign - 1) * fdShift); SabrParametersSwaptionVolatilities calibratedShifted = SABR_CALIBRATION.calibrateWithFixedBetaAndShift(DEFINITION, CALIBRATION_TIME, dataShifted, MULTICURVE, BETA_SURFACE, SHIFT_SABR_SURFACE); pv[loopsign] = LEG_PRICER.presentValue(FLOOR_LEG, MULTICURVE, calibratedShifted).Amount; } double sensitivityFd = (pv[1] - pv[0]) / (2 * fdShift); // FD sensitivity computation SabrSwaptionCalibratorSmileTestUtils.checkAcceptable(sensitivityFd, sensitivityArray.get(surfacePointIndex), 0.10, "Tenor/Expiry: " + TENORS.get(looptenor) + " / " + EXPIRIES.get(loopexpiry)); surfacePointIndex++; } } } }
private static TenorRawOptionData rawData(double[][][] dataArray) { IDictionary <Tenor, RawOptionData> raw = new SortedDictionary <Tenor, RawOptionData>(); for (int looptenor = 0; looptenor < dataArray.Length; looptenor++) { DoubleMatrix matrix = DoubleMatrix.ofUnsafe(dataArray[looptenor]); raw[TENORS.get(looptenor)] = RawOptionData.of(EXPIRIES, MONEYNESS, SIMPLE_MONEYNESS, matrix, NORMAL_VOLATILITY); } return(TenorRawOptionData.of(raw)); }
/// <summary> /// Create a <seealso cref="RawOptionData"/> object for calibration from data. /// </summary> /// <param name="tenors"> the list of tenors </param> /// <param name="expiries"> the list of expiries </param> /// <param name="strikeLikeType"> the type of the strike-like dimension </param> /// <param name="strikeLikeData"> the data related to the strike-like dimension </param> /// <param name="dataType"> the type of the data </param> /// <param name="dataArray"> the array with the raw data, including potential Double.NaN for missing data. </param> /// <returns> the raw option data object </returns> public static TenorRawOptionData rawData(IList <Tenor> tenors, IList <Period> expiries, ValueType strikeLikeType, DoubleArray strikeLikeData, ValueType dataType, double[][][] dataArray) { IDictionary <Tenor, RawOptionData> raw = new SortedDictionary <Tenor, RawOptionData>(); for (int looptenor = 0; looptenor < dataArray.Length; looptenor++) { DoubleMatrix matrix = DoubleMatrix.ofUnsafe(dataArray[looptenor]); raw[tenors[looptenor]] = RawOptionData.of(expiries, strikeLikeData, strikeLikeType, matrix, dataType); } return(TenorRawOptionData.of(raw)); }
/// <summary> /// Create a <seealso cref="RawOptionData"/> object for calibration from data and shift one point. /// </summary> /// <param name="tenors"> the list of tenors </param> /// <param name="expiries"> the list of expiries </param> /// <param name="strikeLikeType"> the type of the strike-like dimension </param> /// <param name="strikeLikeData"> the data related to the strike-like dimension </param> /// <param name="dataType"> the type of the data </param> /// <param name="dataArray"> the array with the raw data, including potential Double.NaN for missing data. </param> /// <param name="i"> the index of the tenor to shift </param> /// <param name="j"> the index of the expiry to shift </param> /// <param name="k"> the index of the strike-like dimension to shift </param> /// <param name="shift"> the size of the shift </param> /// <returns> the raw option data object </returns> public static TenorRawOptionData rawDataShiftPoint(IList <Tenor> tenors, IList <Period> expiries, ValueType strikeLikeType, DoubleArray strikeLikeData, ValueType dataType, double[][][] dataArray, int i, int j, int k, double shift) { IDictionary <Tenor, RawOptionData> raw = new SortedDictionary <Tenor, RawOptionData>(); for (int looptenor = 0; looptenor < dataArray.Length; looptenor++) { double[][] shiftedData = java.util.dataArray[looptenor].Select(row => row.clone()).ToArray(l => new double[l][]); // deep copy of 2d array if (looptenor == i) { shiftedData[j][k] += shift; } DoubleMatrix matrix = DoubleMatrix.ofUnsafe(shiftedData); raw[tenors[looptenor]] = RawOptionData.of(expiries, strikeLikeData, strikeLikeType, matrix, dataType); } return(TenorRawOptionData.of(raw)); }
//------------------------------------------------------------------------- /// <summary> /// Runs the calibration of swaptions and print the calibrated smile results on the console. /// </summary> /// <param name="args"> -s to use the sparse data, i.e. a cube with missing data points </param> public static void Main(string[] args) { // select data TenorRawOptionData data = DATA_FULL; if (args.Length > 0) { if (args[0].Equals("-s")) { data = DATA_SPARSE; } } Console.WriteLine("Start calibration"); double beta = 0.50; SurfaceMetadata betaMetadata = DefaultSurfaceMetadata.builder().xValueType(ValueType.YEAR_FRACTION).yValueType(ValueType.YEAR_FRACTION).zValueType(ValueType.SABR_BETA).surfaceName("Beta").build(); Surface betaSurface = ConstantSurface.of(betaMetadata, beta); double shift = 0.0300; Surface shiftSurface = ConstantSurface.of("Shift", shift); SabrParametersSwaptionVolatilities calibrated = SABR_CALIBRATION.calibrateWithFixedBetaAndShift(DEFINITION, CALIBRATION_TIME, data, MULTICURVE, betaSurface, shiftSurface); Console.WriteLine("End calibration"); /* Graph calibration */ int nbStrikesGraph = 50; double moneyMin = -0.0250; double moneyMax = +0.0300; double[] moneyGraph = new double[nbStrikesGraph + 1]; for (int i = 0; i < nbStrikesGraph + 1; i++) { moneyGraph[i] = moneyMin + i * (moneyMax - moneyMin) / nbStrikesGraph; } //JAVA TO C# CONVERTER NOTE: The following call to the 'RectangularArrays' helper class reproduces the rectangular array initialization that is automatic in Java: //ORIGINAL LINE: double[][][] strikesGraph = new double[NB_TENORS][NB_EXPIRIES][nbStrikesGraph + 1]; double[][][] strikesGraph = RectangularArrays.ReturnRectangularDoubleArray(NB_TENORS, NB_EXPIRIES, nbStrikesGraph + 1); //JAVA TO C# CONVERTER NOTE: The following call to the 'RectangularArrays' helper class reproduces the rectangular array initialization that is automatic in Java: //ORIGINAL LINE: double[][][] volLNGraph = new double[NB_TENORS][NB_EXPIRIES][nbStrikesGraph + 1]; double[][][] volLNGraph = RectangularArrays.ReturnRectangularDoubleArray(NB_TENORS, NB_EXPIRIES, nbStrikesGraph + 1); //JAVA TO C# CONVERTER NOTE: The following call to the 'RectangularArrays' helper class reproduces the rectangular array initialization that is automatic in Java: //ORIGINAL LINE: double[][][] volNGraph = new double[NB_TENORS][NB_EXPIRIES][nbStrikesGraph + 1]; double[][][] volNGraph = RectangularArrays.ReturnRectangularDoubleArray(NB_TENORS, NB_EXPIRIES, nbStrikesGraph + 1); //JAVA TO C# CONVERTER NOTE: The following call to the 'RectangularArrays' helper class reproduces the rectangular array initialization that is automatic in Java: //ORIGINAL LINE: double[][] parRate = new double[NB_TENORS][NB_EXPIRIES]; double[][] parRate = RectangularArrays.ReturnRectangularDoubleArray(NB_TENORS, NB_EXPIRIES); for (int looptenor = 0; looptenor < TENORS.size(); looptenor++) { double tenor = TENORS.get(looptenor).get(ChronoUnit.YEARS); for (int loopexpiry = 0; loopexpiry < EXPIRIES.size(); loopexpiry++) { LocalDate expiry = EUR_FIXED_1Y_EURIBOR_6M.FloatingLeg.StartDateBusinessDayAdjustment.adjust(CALIBRATION_DATE.plus(EXPIRIES.get(loopexpiry)), REF_DATA); LocalDate effectiveDate = EUR_FIXED_1Y_EURIBOR_6M.calculateSpotDateFromTradeDate(expiry, REF_DATA); LocalDate endDate = effectiveDate.plus(TENORS.get(looptenor)); SwapTrade swap = EUR_FIXED_1Y_EURIBOR_6M.toTrade(CALIBRATION_DATE, effectiveDate, endDate, BuySell.BUY, 1.0, 0.0); parRate[looptenor][loopexpiry] = SWAP_PRICER.parRate(swap.resolve(REF_DATA).Product, MULTICURVE); ZonedDateTime expiryDateTime = expiry.atTime(11, 0).atZone(ZoneId.of("Europe/Berlin")); double time = calibrated.relativeTime(expiryDateTime); for (int i = 0; i < nbStrikesGraph + 1; i++) { strikesGraph[looptenor][loopexpiry][i] = parRate[looptenor][loopexpiry] + moneyGraph[i]; volLNGraph[looptenor][loopexpiry][i] = calibrated.volatility(expiryDateTime, tenor, strikesGraph[looptenor][loopexpiry][i], parRate[looptenor][loopexpiry]); volNGraph[looptenor][loopexpiry][i] = NormalFormulaRepository.impliedVolatilityFromBlackApproximated(parRate[looptenor][loopexpiry] + shift, strikesGraph[looptenor][loopexpiry][i] + shift, time, volLNGraph[looptenor][loopexpiry][i]); } } } /* Graph export */ string svn = "Moneyness"; for (int looptenor = 0; looptenor < TENORS.size(); looptenor++) { for (int loopexpiry = 0; loopexpiry < EXPIRIES.size(); loopexpiry++) { svn = svn + ", Strike_" + EXPIRIES.get(loopexpiry).ToString() + "x" + TENORS.get(looptenor).ToString() + ", NormalVol_" + EXPIRIES.get(loopexpiry).ToString() + "x" + TENORS.get(looptenor).ToString(); } } svn = svn + "\n"; for (int i = 0; i < nbStrikesGraph + 1; i++) { svn = svn + moneyGraph[i]; for (int looptenor = 0; looptenor < TENORS.size(); looptenor++) { for (int loopexpiry = 0; loopexpiry < EXPIRIES.size(); loopexpiry++) { svn = svn + ", " + strikesGraph[looptenor][loopexpiry][i]; svn = svn + ", " + volNGraph[looptenor][loopexpiry][i]; } } svn = svn + "\n"; } Console.WriteLine(svn); }
/// <summary> /// Check that the sensitivities of parameters with respect to data is stored in the metadata. /// Compare the sensitivities to a finite difference approximation. /// This test is relatively slow as it calibrates the full surface multiple times. /// </summary> //JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes: //ORIGINAL LINE: @Test public void log_normal_cube_sensitivity() public virtual void log_normal_cube_sensitivity() { double beta = 1.0; Surface betaSurface = ConstantSurface.of("Beta", beta).withMetadata(DefaultSurfaceMetadata.builder().xValueType(ValueType.YEAR_FRACTION).yValueType(ValueType.YEAR_FRACTION).zValueType(ValueType.SABR_BETA).surfaceName("Beta").build()); double shift = 0.0000; Surface shiftSurface = ConstantSurface.of("Shift", shift).withMetadata(DefaultSurfaceMetadata.builder().xValueType(ValueType.YEAR_FRACTION).yValueType(ValueType.YEAR_FRACTION).surfaceName("Shift").build()); SabrParametersSwaptionVolatilities calibrated = SABR_CALIBRATION.calibrateWithFixedBetaAndShift(DEFINITION, CALIBRATION_TIME, DATA_SPARSE, MULTICURVE, betaSurface, shiftSurface); double fdShift = 1.0E-5; SurfaceMetadata alphaMetadata = calibrated.Parameters.AlphaSurface.Metadata; Optional <IList <ParameterMetadata> > alphaParameterMetadataOption = alphaMetadata.ParameterMetadata; assertTrue(alphaParameterMetadataOption.Present); IList <ParameterMetadata> alphaParameterMetadata = alphaParameterMetadataOption.get(); IList <DoubleArray> alphaJacobian = calibrated.DataSensitivityAlpha.get(); SurfaceMetadata rhoMetadata = calibrated.Parameters.RhoSurface.Metadata; Optional <IList <ParameterMetadata> > rhoParameterMetadataOption = rhoMetadata.ParameterMetadata; assertTrue(rhoParameterMetadataOption.Present); IList <ParameterMetadata> rhoParameterMetadata = rhoParameterMetadataOption.get(); IList <DoubleArray> rhoJacobian = calibrated.DataSensitivityRho.get(); SurfaceMetadata nuMetadata = calibrated.Parameters.NuSurface.Metadata; Optional <IList <ParameterMetadata> > nuParameterMetadataOption = nuMetadata.ParameterMetadata; assertTrue(nuParameterMetadataOption.Present); IList <ParameterMetadata> nuParameterMetadata = nuParameterMetadataOption.get(); IList <DoubleArray> nuJacobian = calibrated.DataSensitivityNu.get(); int surfacePointIndex = 0; for (int loopexpiry = 0; loopexpiry < EXPIRIES.Count; loopexpiry++) { for (int looptenor = 0; looptenor < TENORS.Count; looptenor++) { Tenor tenor = TENORS[looptenor]; double tenorYears = tenor.get(ChronoUnit.YEARS); LocalDate expiry = EUR_FIXED_1Y_EURIBOR_6M.FloatingLeg.StartDateBusinessDayAdjustment.adjust(CALIBRATION_DATE.plus(EXPIRIES[loopexpiry]), REF_DATA); ZonedDateTime expiryDateTime = expiry.atTime(11, 0).atZone(ZoneId.of("Europe/Berlin")); double time = calibrated.relativeTime(expiryDateTime); Pair <DoubleArray, DoubleArray> ds = DATA_SPARSE.getData(tenor).availableSmileAtExpiry(EXPIRIES[loopexpiry]); if (!ds.First.Empty) { int availableDataIndex = 0; ParameterMetadata alphaPM = alphaParameterMetadata[surfacePointIndex]; assertTrue(alphaPM is SwaptionSurfaceExpiryTenorParameterMetadata); SwaptionSurfaceExpiryTenorParameterMetadata pmAlphaSabr = (SwaptionSurfaceExpiryTenorParameterMetadata)alphaPM; assertEquals(tenorYears, pmAlphaSabr.Tenor); assertEquals(time, pmAlphaSabr.YearFraction, TOLERANCE_EXPIRY); DoubleArray alphaSensitivityToData = alphaJacobian[surfacePointIndex]; ParameterMetadata rhoPM = rhoParameterMetadata[surfacePointIndex]; assertTrue(rhoPM is SwaptionSurfaceExpiryTenorParameterMetadata); SwaptionSurfaceExpiryTenorParameterMetadata pmRhoSabr = (SwaptionSurfaceExpiryTenorParameterMetadata)rhoPM; assertEquals(tenorYears, pmRhoSabr.Tenor); assertEquals(time, pmRhoSabr.YearFraction, TOLERANCE_EXPIRY); DoubleArray rhoSensitivityToData = rhoJacobian[surfacePointIndex]; ParameterMetadata nuPM = nuParameterMetadata[surfacePointIndex]; assertTrue(nuPM is SwaptionSurfaceExpiryTenorParameterMetadata); SwaptionSurfaceExpiryTenorParameterMetadata pmNuSabr = (SwaptionSurfaceExpiryTenorParameterMetadata)nuPM; assertEquals(tenorYears, pmNuSabr.Tenor); assertEquals(time, pmNuSabr.YearFraction, TOLERANCE_EXPIRY); DoubleArray nuSensitivityToData = nuJacobian[surfacePointIndex]; for (int loopmoney = 0; loopmoney < MONEYNESS.size(); loopmoney++) { if (!double.IsNaN(DATA_LOGNORMAL[looptenor][loopexpiry][loopmoney])) { double[] alphaShifted = new double[2]; double[] rhoShifted = new double[2]; double[] nuShifted = new double[2]; for (int loopsign = 0; loopsign < 2; loopsign++) { TenorRawOptionData dataShifted = SabrSwaptionCalibratorSmileTestUtils.rawDataShiftPoint(TENORS, EXPIRIES, ValueType.SIMPLE_MONEYNESS, MONEYNESS, ValueType.BLACK_VOLATILITY, DATA_LOGNORMAL, looptenor, loopexpiry, loopmoney, (2 * loopsign - 1) * fdShift); SabrParametersSwaptionVolatilities calibratedShifted = SABR_CALIBRATION.calibrateWithFixedBetaAndShift(DEFINITION, CALIBRATION_TIME, dataShifted, MULTICURVE, betaSurface, shiftSurface); alphaShifted[loopsign] = calibratedShifted.Parameters.AlphaSurface.zValue(time, tenorYears); rhoShifted[loopsign] = calibratedShifted.Parameters.RhoSurface.zValue(time, tenorYears); nuShifted[loopsign] = calibratedShifted.Parameters.NuSurface.zValue(time, tenorYears); } double alphaSensitivityComputed = alphaSensitivityToData.get(availableDataIndex); double alphaSensitivityExpected = (alphaShifted[1] - alphaShifted[0]) / (2 * fdShift); checkAcceptable(alphaSensitivityComputed, alphaSensitivityExpected, TOLERANCE_PARAM_SENSITIVITY, "Alpha: " + looptenor + " / " + loopexpiry + " / " + loopmoney); double rhoSensitivityComputed = rhoSensitivityToData.get(availableDataIndex); double rhoSensitivityExpected = (rhoShifted[1] - rhoShifted[0]) / (2 * fdShift); checkAcceptable(rhoSensitivityComputed, rhoSensitivityExpected, TOLERANCE_PARAM_SENSITIVITY, "Rho: " + looptenor + " / " + loopexpiry + " / " + loopmoney); double nuSensitivityComputed = nuSensitivityToData.get(availableDataIndex); double nuSensitivityExpected = (nuShifted[1] - nuShifted[0]) / (2 * fdShift); checkAcceptable(nuSensitivityComputed, nuSensitivityExpected, TOLERANCE_PARAM_SENSITIVITY, "Nu: " + looptenor + " / " + loopexpiry + " / " + loopmoney); availableDataIndex++; } } surfacePointIndex++; } } } }
//------------------------------------------------------------------------- /// <summary> /// Runs the calibration of SABR on swaptions and print on the console the present value, bucketed PV01 and /// the bucketed Vega of a 18M x 4Y swaption. /// </summary> /// <param name="args"> -s to use the spares data </param> public static void Main(string[] args) { long start, end; // Swaption description BuySell payer = BuySell.BUY; Period expiry = Period.ofMonths(18); double notional = 1_000_000; double strike = 0.0100; Tenor tenor = Tenor.TENOR_4Y; LocalDate expiryDate = EUR_FIXED_1Y_EURIBOR_6M.FloatingLeg.StartDateBusinessDayAdjustment.adjust(CALIBRATION_DATE.plus(expiry), REF_DATA); SwapTrade underlying = EUR_FIXED_1Y_EURIBOR_6M.createTrade(expiryDate, tenor, payer, notional, strike, REF_DATA); Swaption swaption = Swaption.builder().expiryDate(AdjustableDate.of(expiryDate)).expiryTime(LocalTime.of(11, 0x0)).expiryZone(ZoneId.of("Europe/Berlin")).underlying(underlying.Product).longShort(LongShort.LONG).swaptionSettlement(PhysicalSwaptionSettlement.DEFAULT).build(); ResolvedSwaption resolvedSwaption = swaption.resolve(REF_DATA); // select data TenorRawOptionData data = DATA_FULL; if (args.Length > 0) { if (args[0].Equals("-s")) { data = DATA_SPARSE; } } start = DateTimeHelper.CurrentUnixTimeMillis(); // Curve calibration RatesProvider multicurve = CALIBRATOR.calibrate(CONFIGS, MARKET_QUOTES, REF_DATA); end = DateTimeHelper.CurrentUnixTimeMillis(); Console.WriteLine("Curve calibration time: " + (end - start) + " ms."); // SABR calibration start = DateTimeHelper.CurrentUnixTimeMillis(); double beta = 0.50; SurfaceMetadata betaMetadata = DefaultSurfaceMetadata.builder().xValueType(ValueType.YEAR_FRACTION).yValueType(ValueType.YEAR_FRACTION).zValueType(ValueType.SABR_BETA).surfaceName("Beta").build(); Surface betaSurface = ConstantSurface.of(betaMetadata, beta); double shift = 0.0300; Surface shiftSurface = ConstantSurface.of("SABR-Shift", shift); SabrParametersSwaptionVolatilities sabr = SABR_CALIBRATION.calibrateWithFixedBetaAndShift(DEFINITION, CALIBRATION_TIME, data, multicurve, betaSurface, shiftSurface); end = DateTimeHelper.CurrentUnixTimeMillis(); Console.WriteLine("SABR calibration time: " + (end - start) + " ms."); // Price and risk Console.WriteLine("Risk measures: "); start = DateTimeHelper.CurrentUnixTimeMillis(); CurrencyAmount pv = SWAPTION_PRICER.presentValue(resolvedSwaption, multicurve, sabr); Console.WriteLine(" |-> PV: " + pv.ToString()); PointSensitivities deltaPts = SWAPTION_PRICER.presentValueSensitivityRatesStickyModel(resolvedSwaption, multicurve, sabr).build(); CurrencyParameterSensitivities deltaBucketed = multicurve.parameterSensitivity(deltaPts); Console.WriteLine(" |-> Delta bucketed: " + deltaBucketed.ToString()); PointSensitivities vegaPts = SWAPTION_PRICER.presentValueSensitivityModelParamsSabr(resolvedSwaption, multicurve, sabr).build(); Console.WriteLine(" |-> Vega point: " + vegaPts.ToString()); CurrencyParameterSensitivities vegaBucketed = sabr.parameterSensitivity(vegaPts); for (int i = 0; i < vegaBucketed.size(); i++) { Console.WriteLine(" |-> Vega bucketed: " + vegaBucketed.Sensitivities.get(i)); } end = DateTimeHelper.CurrentUnixTimeMillis(); Console.WriteLine("PV and risk time: " + (end - start) + " ms."); }