/// <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);
                    }
                }
            }
        }
예제 #7
0
        //-------------------------------------------------------------------------
        // 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);
        }
예제 #8
0
        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);
        }