/// <summary> /// Tests the interpolation in the time and strike dimensions. /// </summary> public virtual void volatilityTimeInterpolation() { double forward = 1.40; double timeToExpiry = 0.75; double strike = 1.50; double[] vol050 = SMILE_TERM.VolatilityTerm.get(2).Volatility.toArray(); double[] vol100 = SMILE_TERM.VolatilityTerm.get(3).Volatility.toArray(); double[] vol = new double[vol050.Length]; for (int loopvol = 0; loopvol < vol050.Length; loopvol++) { vol[loopvol] = Math.Sqrt(((vol050[loopvol] * vol050[loopvol] * TIME_TO_EXPIRY.get(2) + vol100[loopvol] * vol100[loopvol] * TIME_TO_EXPIRY.get(3)) / 2.0) / timeToExpiry); } SmileDeltaParameters smile = SmileDeltaParameters.of(timeToExpiry, DELTA, DoubleArray.copyOf(vol)); DoubleArray strikes = smile.strike(forward); double volExpected = INTERPOLATOR_STRIKE.bind(strikes, DoubleArray.copyOf(vol), FLAT, FLAT).interpolate(strike); double volComputed = SMILE_TERM.volatility(timeToExpiry, strike, forward); assertEquals(volComputed, volExpected, TOLERANCE_VOL, "Smile by delta term structure: vol interpolation on strike"); double volTriple = SMILE_TERM.volatility(timeToExpiry, strike, forward); assertEquals(volTriple, volComputed, TOLERANCE_VOL, "Smile by delta term structure: vol interpolation on strike"); InterpolatedStrikeSmileDeltaTermStructure smileTerm2 = InterpolatedStrikeSmileDeltaTermStructure.of(VOLATILITY_TERM, ACT_360); double volComputed2 = smileTerm2.volatility(timeToExpiry, strike, forward); assertEquals(volComputed2, volComputed, TOLERANCE_VOL, "Smile by delta term structure: vol interp on strike"); }
//------------------------------------------------------------------------- public virtual void constructor() { InterpolatedStrikeSmileDeltaTermStructure smileTerm1 = InterpolatedStrikeSmileDeltaTermStructure.of(TIME_TO_EXPIRY, DELTA, ATM, RISK_REVERSAL, STRANGLE, ACT_360); assertEquals(smileTerm1, SMILE_TERM, "Smile by delta term structure: constructor"); InterpolatedStrikeSmileDeltaTermStructure smileTerm2 = InterpolatedStrikeSmileDeltaTermStructure.of(TIME_TO_EXPIRY, DELTA, ATM, RISK_REVERSAL, STRANGLE, ACT_360, INTERPOLATOR_STRIKE, FLAT, FLAT); assertEquals(smileTerm2, SMILE_TERM, "Smile by delta term structure: constructor"); }
//------------------------------------------------------------------------- public virtual void coverage() { coverImmutableBean(SMILE_TERM); InterpolatedStrikeSmileDeltaTermStructure other = InterpolatedStrikeSmileDeltaTermStructure.of(DoubleArray.of(0.1, 0.5), DoubleArray.of(0.25), DoubleMatrix.copyOf(new double[][] { new double[] { 0.15, 0.1, 0.12 }, new double[] { 0.1, 0.07, 0.08 } }), ACT_360, CurveInterpolators.NATURAL_SPLINE, CurveExtrapolators.LINEAR, CurveExtrapolators.LINEAR, CurveInterpolators.NATURAL_SPLINE, CurveExtrapolators.LINEAR, CurveExtrapolators.LINEAR); coverBeanEquals(SMILE_TERM, other); }
public virtual void constructor2() { double[][] vol = new double[NB_EXP][]; for (int loopexp = 0; loopexp < NB_EXP; loopexp++) { vol[loopexp] = VOLATILITY_TERM[loopexp].Volatility.toArray(); } InterpolatedStrikeSmileDeltaTermStructure smileTermVol1 = InterpolatedStrikeSmileDeltaTermStructure.of(TIME_TO_EXPIRY, DELTA, DoubleMatrix.copyOf(vol), ACT_360); assertEquals(smileTermVol1, SMILE_TERM, "Smile by delta term structure: constructor"); InterpolatedStrikeSmileDeltaTermStructure smileTermVol2 = InterpolatedStrikeSmileDeltaTermStructure.of(TIME_TO_EXPIRY, DELTA, DoubleMatrix.copyOf(vol), ACT_360, INTERPOLATOR_STRIKE, FLAT, FLAT); assertEquals(smileTermVol2, SMILE_TERM, "Smile by delta term structure: constructor"); }
public virtual void testWrongDataSize() { double[][] vol = new double[NB_EXP][]; for (int loopexp = 0; loopexp < NB_EXP; loopexp++) { vol[loopexp] = VOLATILITY_TERM[loopexp].Volatility.toArray(); } DoubleArray timeShort = DoubleArray.of(0.10, 0.25, 0.50, 1.00, 2.00); DoubleArray deltaLong = DoubleArray.of(0.10, 0.2, 0.25); DoubleArray delta0 = DoubleArray.of(); assertThrowsIllegalArg(() => InterpolatedStrikeSmileDeltaTermStructure.of(timeShort, DELTA, DoubleMatrix.copyOf(vol), ACT_360)); assertThrowsIllegalArg(() => InterpolatedStrikeSmileDeltaTermStructure.of(TIME_TO_EXPIRY, deltaLong, DoubleMatrix.copyOf(vol), ACT_360)); assertThrowsIllegalArg(() => InterpolatedStrikeSmileDeltaTermStructure.of(TIME_TO_EXPIRY, delta0, DoubleMatrix.copyOf(vol), ACT_360)); DoubleMatrix shortMat = DoubleMatrix.copyOf(new double[][] { new double[] { 0.0300, 0.0100 }, new double[] { 0.0310, 0.0110 }, new double[] { 0.0320, 0.0120 }, new double[] { 0.0330, 0.0130 }, new double[] { 0.0340, 0.0140 } }); DoubleMatrix vec = DoubleMatrix.copyOf(new double[][] { new double[] { 0.0300 }, new double[] { 0.0310 }, new double[] { 0.0320 }, new double[] { 0.0330 }, new double[] { 0.0340 }, new double[] { 0.0340 } }); assertThrowsIllegalArg(() => InterpolatedStrikeSmileDeltaTermStructure.of(timeShort, DELTA, ATM, RISK_REVERSAL, STRANGLE, ACT_360)); assertThrowsIllegalArg(() => InterpolatedStrikeSmileDeltaTermStructure.of(TIME_TO_EXPIRY, deltaLong, ATM, RISK_REVERSAL, STRANGLE, ACT_360)); assertThrowsIllegalArg(() => InterpolatedStrikeSmileDeltaTermStructure.of(TIME_TO_EXPIRY, DELTA, ATM, shortMat, STRANGLE, ACT_360)); assertThrowsIllegalArg(() => InterpolatedStrikeSmileDeltaTermStructure.of(TIME_TO_EXPIRY, DELTA, ATM, RISK_REVERSAL, shortMat, ACT_360)); assertThrowsIllegalArg(() => InterpolatedStrikeSmileDeltaTermStructure.of(TIME_TO_EXPIRY, DELTA, ATM, RISK_REVERSAL, vec, ACT_360)); }
/// <summary> /// Tests the interpolation and its derivative with respect to the data by comparison to finite difference. /// </summary> public virtual void volatilityAjoint() { double forward = 1.40; double[] timeToExpiry = new double[] { 0.75, 1.00, 2.50 }; double[] strike = new double[] { 1.50, 1.70, 2.20 }; double[] tolerance = new double[] { 3e-2, 1e-1, 1e-5 }; int nbTest = strike.Length; double shift = 0.00001; for (int looptest = 0; looptest < nbTest; looptest++) { double vol = SMILE_TERM.volatility(timeToExpiry[looptest], strike[looptest], forward); //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[][] bucketTest = new double[TIME_TO_EXPIRY.size()][2 * DELTA.size() + 1]; double[][] bucketTest = RectangularArrays.ReturnRectangularDoubleArray(TIME_TO_EXPIRY.size(), 2 * DELTA.size() + 1); VolatilityAndBucketedSensitivities volComputed = SMILE_TERM.volatilityAndSensitivities(timeToExpiry[looptest], strike[looptest], forward); DoubleMatrix bucketSensi = volComputed.Sensitivities; assertEquals(volComputed.Volatility, vol, 1.0E-10, "Smile by delta term structure: volatility adjoint"); SmileDeltaParameters[] volData = new SmileDeltaParameters[TIME_TO_EXPIRY.size()]; double[] volBumped = new double[2 * DELTA.size() + 1]; for (int loopexp = 0; loopexp < TIME_TO_EXPIRY.size(); loopexp++) { for (int loopsmile = 0; loopsmile < 2 * DELTA.size() + 1; loopsmile++) { Array.Copy(SMILE_TERM.VolatilityTerm.toArray(), 0, volData, 0, TIME_TO_EXPIRY.size()); Array.Copy(SMILE_TERM.VolatilityTerm.get(loopexp).Volatility.toArray(), 0, volBumped, 0, 2 * DELTA.size() + 1); volBumped[loopsmile] += shift; volData[loopexp] = SmileDeltaParameters.of(TIME_TO_EXPIRY.get(loopexp), DELTA, DoubleArray.copyOf(volBumped)); InterpolatedStrikeSmileDeltaTermStructure smileTermBumped = InterpolatedStrikeSmileDeltaTermStructure.of(ImmutableList.copyOf(volData), ACT_360); bucketTest[loopexp][loopsmile] = (smileTermBumped.volatility(timeToExpiry[looptest], strike[looptest], forward) - volComputed.Volatility) / shift; assertEquals(bucketSensi.get(loopexp, loopsmile), bucketTest[loopexp][loopsmile], tolerance[looptest], "Smile by delta term structure: (test: " + looptest + ") volatility bucket sensitivity " + loopexp + " - " + loopsmile); } } } }
//------------------------------------------------------------------------- // bumping a node point at (nodeExpiry, nodeDelta) private double nodeSensitivity(BlackFxOptionSmileVolatilities provider, CurrencyPair pair, ZonedDateTime expiry, double strike, double forward, double nodeExpiry, double nodeDelta) { double strikeMod = provider.CurrencyPair.Equals(pair) ? strike : 1.0 / strike; double forwardMod = provider.CurrencyPair.Equals(pair) ? forward : 1.0 / forward; InterpolatedStrikeSmileDeltaTermStructure smileTerm = (InterpolatedStrikeSmileDeltaTermStructure)provider.Smile; double[] times = smileTerm.Expiries.toArray(); int nTimes = times.Length; SmileDeltaParameters[] volTermUp = new SmileDeltaParameters[nTimes]; SmileDeltaParameters[] volTermDw = new SmileDeltaParameters[nTimes]; int deltaIndex = -1; for (int i = 0; i < nTimes; ++i) { DoubleArray deltas = smileTerm.VolatilityTerm.get(i).Delta; int nDeltas = deltas.size(); int nDeltasTotal = 2 * nDeltas + 1; double[] deltasTotal = new double[nDeltasTotal]; deltasTotal[nDeltas] = 0.5d; for (int j = 0; j < nDeltas; ++j) { deltasTotal[j] = 1d - deltas.get(j); deltasTotal[2 * nDeltas - j] = deltas.get(j); } double[] volsUp = smileTerm.VolatilityTerm.get(i).Volatility.toArray(); double[] volsDw = smileTerm.VolatilityTerm.get(i).Volatility.toArray(); if (Math.Abs(times[i] - nodeExpiry) < TOLERANCE) { for (int j = 0; j < nDeltasTotal; ++j) { if (Math.Abs(deltasTotal[j] - nodeDelta) < TOLERANCE) { deltaIndex = j; volsUp[j] += EPS; volsDw[j] -= EPS; } } } volTermUp[i] = SmileDeltaParameters.of(times[i], deltas, DoubleArray.copyOf(volsUp)); volTermDw[i] = SmileDeltaParameters.of(times[i], deltas, DoubleArray.copyOf(volsDw)); } InterpolatedStrikeSmileDeltaTermStructure smileTermUp = InterpolatedStrikeSmileDeltaTermStructure.of(ImmutableList.copyOf(volTermUp), ACT_365F); InterpolatedStrikeSmileDeltaTermStructure smileTermDw = InterpolatedStrikeSmileDeltaTermStructure.of(ImmutableList.copyOf(volTermDw), ACT_365F); BlackFxOptionSmileVolatilities provUp = BlackFxOptionSmileVolatilities.of(NAME, CURRENCY_PAIR, VAL_DATE_TIME, smileTermUp); BlackFxOptionSmileVolatilities provDw = BlackFxOptionSmileVolatilities.of(NAME, CURRENCY_PAIR, VAL_DATE_TIME, smileTermDw); double volUp = provUp.volatility(pair, expiry, strike, forward); double volDw = provDw.volatility(pair, expiry, strike, forward); double totalSensi = 0.5 * (volUp - volDw) / EPS; double expiryTime = provider.relativeTime(expiry); SmileDeltaParameters singleSmile = smileTerm.smileForExpiry(expiryTime); double[] strikesUp = singleSmile.strike(forwardMod).toArray(); double[] strikesDw = strikesUp.Clone(); double[] vols = singleSmile.Volatility.toArray(); strikesUp[deltaIndex] += EPS; strikesDw[deltaIndex] -= EPS; double volStrikeUp = LINEAR.bind(DoubleArray.ofUnsafe(strikesUp), DoubleArray.ofUnsafe(vols), FLAT, FLAT).interpolate(strikeMod); double volStrikeDw = LINEAR.bind(DoubleArray.ofUnsafe(strikesDw), DoubleArray.ofUnsafe(vols), FLAT, FLAT).interpolate(strikeMod); double sensiStrike = 0.5 * (volStrikeUp - volStrikeDw) / EPS; SmileDeltaParameters singleSmileUp = smileTermUp.smileForExpiry(expiryTime); double strikeUp = singleSmileUp.strike(forwardMod).get(deltaIndex); SmileDeltaParameters singleSmileDw = smileTermDw.smileForExpiry(expiryTime); double strikeDw = singleSmileDw.strike(forwardMod).get(deltaIndex); double sensiVol = 0.5 * (strikeUp - strikeDw) / EPS; return(totalSensi - sensiStrike * sensiVol); }
static FxOptionVolatilitiesMarketDataFunctionTest() { ImmutableList.Builder <FxOptionVolatilitiesNode> volNodeBuilder = ImmutableList.builder(); ImmutableMap.Builder <QuoteId, double> marketQuoteBuilder = ImmutableMap.builder(); ImmutableMap.Builder <QuoteId, MarketDataBox <double> > scenarioMarketQuoteBuilder = ImmutableMap.builder(); ImmutableList.Builder <FixedOvernightSwapCurveNode> usdNodeBuilder = ImmutableList.builder(); ImmutableList.Builder <FxSwapCurveNode> gbpNodeBuilder = ImmutableList.builder(); for (int i = 0; i < VOL_TENORS.Count; ++i) { for (int j = 0; j < STRIKES.Count; ++j) { QuoteId quoteId = QuoteId.of(StandardId.of("OG", VOL_TENORS[i].ToString() + "_" + STRIKES[j].Label + "_" + VALUE_TYPES[j].ToString())); volNodeBuilder.add(FxOptionVolatilitiesNode.of(GBP_USD, SPOT_OFFSET, BDA, VALUE_TYPES[j], quoteId, VOL_TENORS[i], STRIKES[j])); marketQuoteBuilder.put(quoteId, VOL_QUOTES[i][j]); scenarioMarketQuoteBuilder.put(quoteId, MarketDataBox.ofScenarioValues(VOL_QUOTES[i][j], VOL_QUOTES_1[i][j])); } } for (int i = 0; i < USD_QUOTES.Count; ++i) { QuoteId quoteId = QuoteId.of(StandardId.of("OG", USD.ToString() + "-OIS-" + USD_TENORS[i].ToString())); usdNodeBuilder.add(FixedOvernightSwapCurveNode.of(FixedOvernightSwapTemplate.of(USD_TENORS[i], FixedOvernightSwapConventions.USD_FIXED_TERM_FED_FUND_OIS), quoteId)); marketQuoteBuilder.put(quoteId, USD_QUOTES[i]); scenarioMarketQuoteBuilder.put(quoteId, MarketDataBox.ofScenarioValues(USD_QUOTES[i], USD_QUOTES_1[i])); } for (int i = 0; i < GBP_QUOTES.Count; ++i) { QuoteId quoteId = QuoteId.of(StandardId.of("OG", GBP_USD.ToString() + "-FX-" + GBP_PERIODS[i].ToString())); gbpNodeBuilder.add(FxSwapCurveNode.of(FxSwapTemplate.of(GBP_PERIODS[i], FxSwapConventions.GBP_USD), quoteId)); marketQuoteBuilder.put(quoteId, GBP_QUOTES[i]); scenarioMarketQuoteBuilder.put(quoteId, MarketDataBox.ofScenarioValues(GBP_QUOTES[i], GBP_QUOTES_1[i])); } VOL_NODES = volNodeBuilder.build(); USD_NODES = usdNodeBuilder.build(); GBP_NODES = gbpNodeBuilder.build(); MARKET_QUOTES = marketQuoteBuilder.build(); SCENARIO_MARKET_QUOTES = scenarioMarketQuoteBuilder.build(); IList <double> expiry = VOL_TENORS.Select(t => ACT_365F.relativeYearFraction(VALUATION_DATE, BDA.adjust(SPOT_OFFSET.adjust(VALUATION_DATE, REF_DATA).plus(t), REF_DATA))).ToList(); int nSmiles = expiry.Count; double[] atm = new double[nSmiles]; //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[][] rr = new double[nSmiles][2]; double[][] rr = RectangularArrays.ReturnRectangularDoubleArray(nSmiles, 2); //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[][] str = new double[nSmiles][2]; double[][] str = RectangularArrays.ReturnRectangularDoubleArray(nSmiles, 2); for (int i = 0; i < nSmiles; ++i) { atm[i] = VOL_QUOTES[i][0]; rr[i][0] = VOL_QUOTES[i][1]; rr[i][1] = VOL_QUOTES[i][3]; str[i][0] = VOL_QUOTES[i][2]; str[i][1] = VOL_QUOTES[i][4]; } InterpolatedStrikeSmileDeltaTermStructure term = InterpolatedStrikeSmileDeltaTermStructure.of(DoubleArray.copyOf(expiry), DoubleArray.of(0.1, 0.25), DoubleArray.copyOf(atm), DoubleMatrix.copyOf(rr), DoubleMatrix.copyOf(str), ACT_365F, LINEAR, FLAT, FLAT, PCHIP, FLAT, FLAT); EXP_VOLS = BlackFxOptionSmileVolatilities.of(VOL_NAME, GBP_USD, VALUATION_DATE.atTime(VALUATION_TIME).atZone(ZONE), term); for (int i = 0; i < nSmiles; ++i) { atm[i] = VOL_QUOTES_1[i][0]; rr[i][0] = VOL_QUOTES_1[i][1]; rr[i][1] = VOL_QUOTES_1[i][3]; str[i][0] = VOL_QUOTES_1[i][2]; str[i][1] = VOL_QUOTES_1[i][4]; } InterpolatedStrikeSmileDeltaTermStructure term1 = InterpolatedStrikeSmileDeltaTermStructure.of(DoubleArray.copyOf(expiry), DoubleArray.of(0.1, 0.25), DoubleArray.copyOf(atm), DoubleMatrix.copyOf(rr), DoubleMatrix.copyOf(str), ACT_365F, LINEAR, FLAT, FLAT, PCHIP, FLAT, FLAT); EXP_VOLS_1 = BlackFxOptionSmileVolatilities.of(VOL_NAME, GBP_USD, VALUATION_DATE_1.atTime(VALUATION_TIME_1).atZone(ZONE), term1); ImmutableList.Builder <FxOptionVolatilitiesNode> nodeBuilder = ImmutableList.builder(); ImmutableMap.Builder <QuoteId, double> quoteBuilder = ImmutableMap.builder(); for (int i = 0; i < SURFACE_TENORS.Count; ++i) { for (int j = 0; j < SURFACE_STRIKES.Count; ++j) { QuoteId quoteId = QuoteId.of(StandardId.of("OG", GBP_USD.ToString() + "_" + SURFACE_TENORS[i].ToString() + "_" + SURFACE_STRIKES[j])); quoteBuilder.put(quoteId, SURFACE_VOL_QUOTES[i][j]); nodeBuilder.add(FxOptionVolatilitiesNode.of(GBP_USD, SPOT_OFFSET, BDA, ValueType.BLACK_VOLATILITY, quoteId, SURFACE_TENORS[i], SimpleStrike.of(SURFACE_STRIKES[j]))); } } SURFACE_NODES = nodeBuilder.build(); SURFACE_QUOTES = quoteBuilder.build(); IList <double> expiry = new List <double>(); IList <double> strike = new List <double>(); IList <double> vols = new List <double>(); for (int i = 0; i < SURFACE_TENORS.Count; ++i) { for (int j = 0; j < SURFACE_STRIKES.Count; ++j) { double yearFraction = ACT_365F.relativeYearFraction(VALUATION_DATE, BDA.adjust(SPOT_OFFSET.adjust(VALUATION_DATE, REF_DATA).plus(SURFACE_TENORS[i]), REF_DATA)); expiry.Add(yearFraction); strike.Add(SURFACE_STRIKES[j]); vols.Add(SURFACE_VOL_QUOTES[i][j]); } } SurfaceInterpolator interp = GridSurfaceInterpolator.of(LINEAR, PCHIP); InterpolatedNodalSurface surface = InterpolatedNodalSurface.ofUnsorted(Surfaces.blackVolatilityByExpiryStrike(VOL_NAME.Name, ACT_365F), DoubleArray.copyOf(expiry), DoubleArray.copyOf(strike), DoubleArray.copyOf(vols), interp); SURFACE_EXP_VOLS = BlackFxOptionSurfaceVolatilities.of(VOL_NAME, GBP_USD, VALUATION_DATE.atTime(VALUATION_TIME).atZone(ZONE), surface); }