예제 #1
0
 /// <summary>
 /// Greeks against finite difference approximation.
 /// </summary>
 public virtual void greekfdTest()
 {
     foreach (SimpleConstantContinuousBarrier barrier in BARRIERS)
     {
         ValueDerivatives computed = PRICER.priceAdjoint(SPOT, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, barrier);
         double           spotUp   = PRICER.price(SPOT + EPS_FD, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, barrier);
         double           spotDw   = PRICER.price(SPOT - EPS_FD, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, barrier);
         double           rateUp   = PRICER.price(SPOT, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM + EPS_FD, VOLATILITY, barrier);
         double           rateDw   = PRICER.price(SPOT, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM - EPS_FD, VOLATILITY, barrier);
         double           costUp   = PRICER.price(SPOT, EXPIRY_TIME, COST_OF_CARRY + EPS_FD, RATE_DOM, VOLATILITY, barrier);
         double           costDw   = PRICER.price(SPOT, EXPIRY_TIME, COST_OF_CARRY - EPS_FD, RATE_DOM, VOLATILITY, barrier);
         double           volUp    = PRICER.price(SPOT, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY + EPS_FD, barrier);
         double           volDw    = PRICER.price(SPOT, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY - EPS_FD, barrier);
         double           timeUp   = PRICER.price(SPOT, EXPIRY_TIME + EPS_FD, COST_OF_CARRY, RATE_DOM, VOLATILITY, barrier);
         double           timeDw   = PRICER.price(SPOT, EXPIRY_TIME - EPS_FD, COST_OF_CARRY, RATE_DOM, VOLATILITY, barrier);
         ValueDerivatives spotUp1  = PRICER.priceAdjoint(SPOT + EPS_FD, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, barrier);
         ValueDerivatives spotDw1  = PRICER.priceAdjoint(SPOT - EPS_FD, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, barrier);
         assertEquals(computed.getDerivative(0), 0.5 * (spotUp - spotDw) / EPS_FD, EPS_FD);
         assertEquals(computed.getDerivative(1), 0.5 * (rateUp - rateDw) / EPS_FD, EPS_FD);
         assertEquals(computed.getDerivative(2), 0.5 * (costUp - costDw) / EPS_FD, EPS_FD);
         assertEquals(computed.getDerivative(3), 0.5 * (volUp - volDw) / EPS_FD, EPS_FD);
         assertEquals(computed.getDerivative(4), 0.5 * (timeUp - timeDw) / EPS_FD, EPS_FD);
         assertEquals(computed.getDerivative(5), 0.5 * (spotUp1.getDerivative(0) - spotDw1.getDerivative(0)) / EPS_FD, EPS_FD);
     }
 }
예제 #2
0
            protected internal override double doFirstDerivative(double xValue)
            {
                ArgChecker.isTrue(Math.Abs(xValue) > SMALL, "magnitude of xValue must not be small");
                ValueDerivatives resValue = FUNCTION.evaluateAndDifferentiate(poly, xValue);

                return(-resValue.Value / (xValue * xValue) + resValue.getDerivative(0) / xValue);
            }
        /// <summary>
        /// Computes the option price derivative with respect to the SABR parameters.
        /// <para>
        /// The price is SABR below the cut-off strike and extrapolated beyond.
        ///
        /// </para>
        /// </summary>
        /// <param name="strike">  the strike of the option </param>
        /// <param name="putCall">  whether the option is put or call </param>
        /// <returns> the option and its derivative </returns>
        public ValueDerivatives priceAdjointSabr(double strike, PutCall putCall)
        {
            double[] priceDerivativeSabr = new double[4];
            double   price;

            if (strike <= cutOffStrike)
            {     // Uses Hagan et al SABR function.
                ValueDerivatives volatilityA = sabrFunction.volatilityAdjoint(forward, strike, timeToExpiry, sabrData);
                ValueDerivatives pA          = BlackFormulaRepository.priceAdjoint(forward, strike, timeToExpiry, volatilityA.Value, putCall == PutCall.CALL);
                price = pA.Value;
                for (int loopparam = 0; loopparam < 4; loopparam++)
                {
                    priceDerivativeSabr[loopparam] = pA.getDerivative(3) * volatilityA.getDerivative(loopparam + 2);
                }
            }
            else
            {     // Uses extrapolation for call.
                if (parameterDerivativeSabr == null)
                {
                    parameterDerivativeSabr = computesParametersDerivativeSabr();
                    // Derivatives computed only once and only when required
                }
                double f   = extrapolation(strike);
                double fDa = f;
                double fDb = f / strike;
                double fDc = fDb / strike;
                price = putCall.Call ? f : f - forward + strike;   // Put by call/put parity
                for (int loopparam = 0; loopparam < 4; loopparam++)
                {
                    priceDerivativeSabr[loopparam] = fDa * parameterDerivativeSabr[loopparam][0] + fDb * parameterDerivativeSabr[loopparam][1] + fDc * parameterDerivativeSabr[loopparam][2];
                }
            }
            return(ValueDerivatives.of(price, DoubleArray.ofUnsafe(priceDerivativeSabr)));
        }
        public virtual void swapRateDa()
        {
            double           shift            = 1.0E-8;
            double           x                = 0.0;
            ValueDerivatives computed         = MODEL.swapRateDaf1(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR);
            double           swapRateComputed = computed.Value;

            double[] dafComputed      = computed.Derivatives.toArray();
            double   swapRateExpected = MODEL.swapRate(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR);

            assertEquals(swapRateComputed, swapRateExpected, TOLERANCE_RATE);
            double[] dafExpected = new double[ALPHA_FIXED.size()];
            for (int loopcf = 0; loopcf < ALPHA_FIXED.size(); loopcf++)
            {
                double[] afBumped = ALPHA_FIXED.toArray();
                afBumped[loopcf] += shift;
                double swapRatePlus = MODEL.swapRate(x, DCF_FIXED, DoubleArray.copyOf(afBumped), DCF_IBOR, ALPHA_IBOR);
                afBumped[loopcf] -= 2 * shift;
                double swapRateMinus = MODEL.swapRate(x, DCF_FIXED, DoubleArray.copyOf(afBumped), DCF_IBOR, ALPHA_IBOR);
                dafExpected[loopcf] = (swapRatePlus - swapRateMinus) / (2 * shift);
            }
            assertTrue(DoubleArrayMath.fuzzyEquals(dafExpected, dafComputed, TOLERANCE_RATE_DELTA));
            double[] daiExpected = new double[DCF_IBOR.size()];
            for (int loopcf = 0; loopcf < DCF_IBOR.size(); loopcf++)
            {
                double[] aiBumped = ALPHA_IBOR.toArray();
                aiBumped[loopcf] += shift;
                double swapRatePlus = MODEL.swapRate(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, DoubleArray.copyOf(aiBumped));
                aiBumped[loopcf] -= 2 * shift;
                double swapRateMinus = MODEL.swapRate(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, DoubleArray.copyOf(aiBumped));
                daiExpected[loopcf] = (swapRatePlus - swapRateMinus) / (2 * shift);
            }
            double[] daiComputed = MODEL.swapRateDai1(x, DCF_FIXED, ALPHA_FIXED, DCF_IBOR, ALPHA_IBOR).Derivatives.toArray();
            assertTrue(DoubleArrayMath.fuzzyEquals(daiExpected, daiComputed, TOLERANCE_RATE_DELTA));
        }
        public virtual DeformedSurface localVolatilityFromPrice(Surface callPriceSurface, double spot, System.Func <double, double> interestRate, System.Func <double, double> dividendRate)
        {
            System.Func <DoublesPair, ValueDerivatives> func = (DoublesPair x) =>
            {
                double      t          = x.First;
                double      k          = x.Second;
                double      r          = interestRate(t);
                double      q          = dividendRate(t);
                double      price      = callPriceSurface.zValue(t, k);
                DoubleArray priceSensi = callPriceSurface.zValueParameterSensitivity(t, k).Sensitivity;
                double      divT       = FIRST_DERIV.differentiate(u => callPriceSurface.zValue(u, k)).apply(t);
                DoubleArray divTSensi  = FIRST_DERIV_SENSI.differentiate(u => callPriceSurface.zValueParameterSensitivity(u.get(0), k).Sensitivity).apply(DoubleArray.of(t)).column(0);
                double      divK       = FIRST_DERIV.differentiate(l => callPriceSurface.zValue(t, l)).apply(k);
                DoubleArray divKSensi  = FIRST_DERIV_SENSI.differentiate(l => callPriceSurface.zValueParameterSensitivity(t, l.get(0)).Sensitivity).apply(DoubleArray.of(k)).column(0);
                double      divK2      = SECOND_DERIV.differentiate(l => callPriceSurface.zValue(t, l)).apply(k);
                DoubleArray divK2Sensi = SECOND_DERIV_SENSI.differentiateNoCross(l => callPriceSurface.zValueParameterSensitivity(t, l.get(0)).Sensitivity).apply(DoubleArray.of(k)).column(0);
                double      var        = 2d * (divT + q * price + (r - q) * k * divK) / (k * k * divK2);
                if (var < 0d)
                {
                    throw new System.ArgumentException("Negative variance");
                }
                double      localVol      = Math.Sqrt(var);
                double      factor        = 1d / (localVol * k * k * divK2);
                DoubleArray localVolSensi = divTSensi.multipliedBy(factor).plus(divKSensi.multipliedBy((r - q) * k * factor)).plus(priceSensi.multipliedBy(q * factor)).plus(divK2Sensi.multipliedBy(-0.5 * localVol / divK2));
                return(ValueDerivatives.of(localVol, localVolSensi));
            };
            SurfaceMetadata metadata = DefaultSurfaceMetadata.builder().xValueType(ValueType.YEAR_FRACTION).yValueType(ValueType.STRIKE).zValueType(ValueType.LOCAL_VOLATILITY).surfaceName(SurfaceName.of("localVol_" + callPriceSurface.Name)).build();

            return(DeformedSurface.of(metadata, callPriceSurface, func));
        }
        // Test getDelta, getGamma and getVega
        public virtual void greeksTest()
        {
            double tol = 1.0e-12;
            double eps = 1.0e-5;

            EuropeanVanillaOption[] options = new EuropeanVanillaOption[] { ITM_CALL, ITM_PUT, OTM_CALL, OTM_PUT, ATM_CALL, ATM_PUT };
            foreach (EuropeanVanillaOption option in options)
            {
                // consistency with getPriceFunction for first order derivatives
                ValueDerivatives price = FUNCTION.getPriceAdjoint(option, VOL_DATA);
                double           delta = FUNCTION.getDelta(option, VOL_DATA);
                double           vega  = FUNCTION.getVega(option, VOL_DATA);
                assertEquals(price.getDerivative(0), delta, tol);
                assertEquals(price.getDerivative(1), vega, tol);

                // testing second order derivative against finite difference approximation
                NormalFunctionData dataUp  = NormalFunctionData.of(F + eps, DF, SIGMA);
                NormalFunctionData dataDw  = NormalFunctionData.of(F - eps, DF, SIGMA);
                double             deltaUp = FUNCTION.getDelta(option, dataUp);
                double             deltaDw = FUNCTION.getDelta(option, dataDw);
                double             @ref    = 0.5 * (deltaUp - deltaDw) / eps;
                double             gamma   = FUNCTION.getGamma(option, VOL_DATA);
                assertEquals(gamma, @ref, eps);

                EuropeanVanillaOption optionUp = EuropeanVanillaOption.of(option.Strike, T + eps, option.PutCall);
                EuropeanVanillaOption optionDw = EuropeanVanillaOption.of(option.Strike, T - eps, option.PutCall);
                double priceTimeUp             = FUNCTION.getPriceFunction(optionUp).apply(VOL_DATA);
                double priceTimeDw             = FUNCTION.getPriceFunction(optionDw).apply(VOL_DATA);
                @ref = -0.5 * (priceTimeUp - priceTimeDw) / eps;
                double theta = FUNCTION.getTheta(option, VOL_DATA);
                assertEquals(theta, @ref, eps);
            }
        }
        /// <summary>
        /// Test the future convexity adjustment factor v a hard-coded value.
        /// </summary>
        public virtual void futureConvexityFactor()
        {
            LocalDate SPOT_DATE         = LocalDate.of(2012, 9, 19);
            LocalDate LAST_TRADING_DATE = EURIBOR3M.calculateFixingFromEffective(SPOT_DATE, REF_DATA);
            LocalDate REFERENCE_DATE    = LocalDate.of(2010, 8, 18);
            double    tradeLastTime     = DayCounts.ACT_ACT_ISDA.relativeYearFraction(REFERENCE_DATE, LAST_TRADING_DATE);
            double    fixStartTime      = DayCounts.ACT_ACT_ISDA.relativeYearFraction(REFERENCE_DATE, SPOT_DATE);
            double    fixEndTime        = DayCounts.ACT_ACT_ISDA.relativeYearFraction(REFERENCE_DATE, EURIBOR3M.calculateMaturityFromEffective(SPOT_DATE, REF_DATA));
            double    factor            = MODEL.futuresConvexityFactor(MODEL_PARAMETERS, tradeLastTime, fixStartTime, fixEndTime);
            double    expectedFactor    = 1.000079130767980;

            assertEquals(expectedFactor, factor, TOLERANCE_RATE);
            // Derivative with respect to volatility parameters
            int nbSigma = MODEL_PARAMETERS.Volatility.size();
            ValueDerivatives factorDeriv = MODEL.futuresConvexityFactorAdjoint(MODEL_PARAMETERS, tradeLastTime, fixStartTime, fixEndTime);
            double           factor2     = factorDeriv.Value;

            double[] sigmaBar = factorDeriv.Derivatives.toArray();
            assertEquals(factor, factor2, TOLERANCE_RATE);
            double[] sigmaBarExpected = new double[nbSigma];
            double   shift            = 1E-6;

            for (int loops = 0; loops < nbSigma; loops++)
            {
                double[] volBumped = VOLATILITY.toArray();
                volBumped[loops] += shift;
                HullWhiteOneFactorPiecewiseConstantParameters parametersBumped = HullWhiteOneFactorPiecewiseConstantParameters.of(MEAN_REVERSION, DoubleArray.copyOf(volBumped), VOLATILITY_TIME);
                double factorPlus = MODEL.futuresConvexityFactor(parametersBumped, tradeLastTime, fixStartTime, fixEndTime);
                volBumped[loops] -= 2 * shift;
                parametersBumped  = HullWhiteOneFactorPiecewiseConstantParameters.of(MEAN_REVERSION, DoubleArray.copyOf(volBumped), VOLATILITY_TIME);
                double factorMinus = MODEL.futuresConvexityFactor(parametersBumped, tradeLastTime, fixStartTime, fixEndTime);
                sigmaBarExpected[loops] = (factorPlus - factorMinus) / (2 * shift);
                assertEquals(sigmaBarExpected[loops], sigmaBar[loops], TOLERANCE_RATE);
            }
        }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the present value sensitivity of the swaption product to the rate curves.
        /// <para>
        /// The present value sensitivity is computed in a "sticky model parameter" style, i.e. the sensitivity to the
        /// curve nodes with the SABR model parameters unchanged. This sensitivity does not include a potential
        /// re-calibration of the model parameters to the raw market data.
        ///
        /// </para>
        /// </summary>
        /// <param name="swaption">  the swaption product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="swaptionVolatilities">  the volatilities </param>
        /// <returns> the point sensitivity to the rate curves </returns>
        public virtual PointSensitivityBuilder presentValueSensitivityRatesStickyModel(ResolvedSwaption swaption, RatesProvider ratesProvider, SabrSwaptionVolatilities swaptionVolatilities)
        {
            validate(swaption, ratesProvider, swaptionVolatilities);
            ZonedDateTime   expiryDateTime = swaption.Expiry;
            double          expiry         = swaptionVolatilities.relativeTime(expiryDateTime);
            ResolvedSwap    underlying     = swaption.Underlying;
            ResolvedSwapLeg fixedLeg       = this.fixedLeg(underlying);

            if (expiry < 0d)
            {     // Option has expired already
                return(PointSensitivityBuilder.none());
            }
            double           forward       = SwapPricer.parRate(underlying, ratesProvider);
            double           pvbp          = SwapPricer.LegPricer.pvbp(fixedLeg, ratesProvider);
            double           strike        = SwapPricer.LegPricer.couponEquivalent(fixedLeg, ratesProvider, pvbp);
            double           tenor         = swaptionVolatilities.tenor(fixedLeg.StartDate, fixedLeg.EndDate);
            double           shift         = swaptionVolatilities.shift(expiry, tenor);
            ValueDerivatives volatilityAdj = swaptionVolatilities.volatilityAdjoint(expiry, tenor, strike, forward);
            bool             isCall        = fixedLeg.PayReceive.Pay;
            // Payer at strike is exercise when rate > strike, i.e. call on rate
            // Backward sweep
            PointSensitivityBuilder pvbpDr    = SwapPricer.LegPricer.pvbpSensitivity(fixedLeg, ratesProvider);
            PointSensitivityBuilder forwardDr = SwapPricer.parRateSensitivity(underlying, ratesProvider);
            double shiftedForward             = forward + shift;
            double shiftedStrike = strike + shift;
            double price         = BlackFormulaRepository.price(shiftedForward, shiftedStrike, expiry, volatilityAdj.Value, isCall);
            double delta         = BlackFormulaRepository.delta(shiftedForward, shiftedStrike, expiry, volatilityAdj.Value, isCall);
            double vega          = BlackFormulaRepository.vega(shiftedForward, shiftedStrike, expiry, volatilityAdj.Value);
            double sign          = swaption.LongShort.sign();

            return(pvbpDr.multipliedBy(price * sign * Math.Sign(pvbp)).combinedWith(forwardDr.multipliedBy((delta + vega * volatilityAdj.getDerivative(0)) * Math.Abs(pvbp) * sign)));
        }
예제 #9
0
        private void testDerivatives(double strike, bool isCall, SimpleConstantContinuousBarrier barrier)
        {
            ValueDerivatives computed = BARRIER_PRICER.priceAdjoint(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, isCall, barrier);
            double           spotUp   = BARRIER_PRICER.price(SPOT + EPS_FD, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, isCall, barrier);
            double           spotDw   = BARRIER_PRICER.price(SPOT - EPS_FD, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, isCall, barrier);
            double           strikeUp = BARRIER_PRICER.price(SPOT, strike + EPS_FD, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, isCall, barrier);
            double           strikeDw = BARRIER_PRICER.price(SPOT, strike - EPS_FD, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, isCall, barrier);
            double           rateUp   = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM + EPS_FD, VOLATILITY, isCall, barrier);
            double           rateDw   = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM - EPS_FD, VOLATILITY, isCall, barrier);
            double           costUp   = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY + EPS_FD, RATE_DOM, VOLATILITY, isCall, barrier);
            double           costDw   = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY - EPS_FD, RATE_DOM, VOLATILITY, isCall, barrier);
            double           volUp    = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY + EPS_FD, isCall, barrier);
            double           volDw    = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY - EPS_FD, isCall, barrier);
            double           timeUp   = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME + EPS_FD, COST_OF_CARRY, RATE_DOM, VOLATILITY, isCall, barrier);
            double           timeDw   = BARRIER_PRICER.price(SPOT, strike, EXPIRY_TIME - EPS_FD, COST_OF_CARRY, RATE_DOM, VOLATILITY, isCall, barrier);
            ValueDerivatives spotUp1  = BARRIER_PRICER.priceAdjoint(SPOT + EPS_FD, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, isCall, barrier);
            ValueDerivatives spotDw1  = BARRIER_PRICER.priceAdjoint(SPOT - EPS_FD, strike, EXPIRY_TIME, COST_OF_CARRY, RATE_DOM, VOLATILITY, isCall, barrier);

            assertEquals(computed.getDerivative(0), 0.5 * (spotUp - spotDw) / EPS_FD, EPS_FD);
            assertEquals(computed.getDerivative(1), 0.5 * (strikeUp - strikeDw) / EPS_FD, EPS_FD);
            assertEquals(computed.getDerivative(2), 0.5 * (rateUp - rateDw) / EPS_FD, EPS_FD);
            assertEquals(computed.getDerivative(3), 0.5 * (costUp - costDw) / EPS_FD, EPS_FD);
            assertEquals(computed.getDerivative(4), 0.5 * (volUp - volDw) / EPS_FD, EPS_FD);
            assertEquals(computed.getDerivative(5), 0.5 * (timeUp - timeDw) / EPS_FD, EPS_FD);
            assertEquals(computed.getDerivative(6), 0.5 * (spotUp1.getDerivative(0) - spotDw1.getDerivative(0)) / EPS_FD, EPS_FD);
        }
예제 #10
0
        /// <summary>
        /// Compute the implied volatility using an approximate explicit transformation formula and its derivative
        /// with respect to the input Black volatility.
        /// <para>
        /// Reference: Hagan, P. S. Volatility conversion calculator. Technical report, Bloomberg.
        ///
        /// </para>
        /// </summary>
        /// <param name="forward">  the forward rate/price </param>
        /// <param name="strike">  the option strike </param>
        /// <param name="timeToExpiry">  the option time to maturity </param>
        /// <param name="blackVolatility">  the Black implied volatility </param>
        /// <returns> the implied volatility and its derivative </returns>
        public static ValueDerivatives impliedVolatilityFromBlackApproximatedAdjoint(double forward, double strike, double timeToExpiry, double blackVolatility)
        {
            ArgChecker.isTrue(strike > 0, "strike must be strictly positive");
            ArgChecker.isTrue(forward > 0, "strike must be strictly positive");
            double lnFK = Math.Log(forward / strike);
            double s2t  = blackVolatility * blackVolatility * timeToExpiry;

            if (Math.Abs((forward - strike) / strike) < ATM_LIMIT)
            {
                double factor1   = Math.Sqrt(forward * strike);
                double factor2   = (1.0d + lnFK * lnFK / 24.0d) / (1.0d + s2t / 24.0d + s2t * s2t / 5670.0d);
                double normalVol = blackVolatility * factor1 * factor2;
                // Backward sweep
                double blackVolatilityBar = factor1 * factor2;
                double factor2Bar         = blackVolatility * factor1;
                double s2tBar             = -(1.0d + lnFK * lnFK / 24.0d) / ((1.0d + s2t / 24.0d + s2t * s2t / 5670.0d) * (1.0d + s2t / 24.0d + s2t * s2t / 5670.0d)) * (1.0d / 24.0d + s2t / 2835.0d) * factor2Bar;
                blackVolatilityBar += 2.0d * blackVolatility * timeToExpiry * s2tBar;
                return(ValueDerivatives.of(normalVol, DoubleArray.of(blackVolatilityBar)));
            }
            double factor1   = (forward - strike) / lnFK;
            double factor2   = 1.0d / (1.0d + (1.0d - lnFK * lnFK / 120.0d) / 24.0d * s2t + s2t * s2t / 5670.0d);
            double normalVol = blackVolatility * factor1 * factor2;
            // Backward sweep
            double blackVolatilityBar = factor1 * factor2;
            double factor2Bar         = blackVolatility * factor1;
            double s2tBar             = -factor2 * factor2 * ((1.0d - lnFK * lnFK / 120.0d) / 24.0d + s2t / 2835.0d) * factor2Bar;

            blackVolatilityBar += 2.0d * blackVolatility * timeToExpiry * s2tBar;
            return(ValueDerivatives.of(normalVol, DoubleArray.of(blackVolatilityBar)));
        }
예제 #11
0
        /// <summary>
        /// Computes the conventional cash annuity for a given yield and its first two derivatives with respect to the yield.
        /// </summary>
        /// <param name="nbPaymentsPerYear">  the number of payment per year </param>
        /// <param name="nbPeriods">  the total number of periods </param>
        /// <param name="yield">  the yield </param>
        /// <returns> the cash annuity and its first two derivatives </returns>
        public virtual ValueDerivatives annuityCash2(int nbPaymentsPerYear, int nbPeriods, double yield)
        {
            double tau = 1d / nbPaymentsPerYear;

            if (Math.Abs(yield) > MIN_YIELD)
            {
                double yieldPerPeriod = yield * tau;
                double dfEnd          = Math.Pow(1d + yieldPerPeriod, -nbPeriods);
                double annuity        = (1d - dfEnd) / yield;
                double derivative1    = -annuity / yield;
                derivative1 += tau * nbPeriods * dfEnd / ((1d + yieldPerPeriod) * yield);
                double derivative2 = -2 * derivative1 / yield;
                derivative2 -= tau * tau * nbPeriods * (nbPeriods + 1) * dfEnd / ((1d + yieldPerPeriod) * (1d + yieldPerPeriod) * yield);
                return(ValueDerivatives.of(annuity, DoubleArray.of(derivative1, derivative2)));
            }
            double annuity           = 0.0d;
            double derivative1       = 0.0d;
            double derivative2       = 0.0d;
            double periodFactor      = 1.0d / (1.0d + yield * tau);
            double multiPeriodFactor = periodFactor;

            for (int i = 0; i < nbPeriods; i++)
            {
                annuity           += multiPeriodFactor;
                multiPeriodFactor *= periodFactor;
                derivative1       += -(i + 1) * multiPeriodFactor;
                derivative2       += (i + 1) * (i + 2) * multiPeriodFactor * periodFactor;
            }
            annuity     *= tau;
            derivative1 *= tau * tau;
            derivative2 *= tau * tau * tau;
            return(ValueDerivatives.of(annuity, DoubleArray.of(derivative1, derivative2)));
        }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Tests of performance. "enabled = false" for the standard testing.
        /// </summary>
//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes:
//ORIGINAL LINE: @Test(enabled = false) public void performanceAlphaAdjoint()
        public virtual void performanceAlphaAdjoint()
        {
            double expiry1 = 0.25;
            double expiry2 = 2.25;
            double numeraire = 10.0;
            double maturity = 9.0;
            int    nbVolatility = VOLATILITY.size();
            long   startTime, endTime;
            int    nbTest = 100000;
            double alpha  = 0.0;

            startTime = DateTimeHelper.CurrentUnixTimeMillis();
            for (int looptest = 0; looptest < nbTest; looptest++)
            {
                alpha = MODEL.alpha(MODEL_PARAMETERS, expiry1, expiry2, numeraire, maturity);
            }
            endTime = DateTimeHelper.CurrentUnixTimeMillis();
            Console.WriteLine(nbTest + " alpha Hull-White: " + (endTime - startTime) + " ms");
            startTime = DateTimeHelper.CurrentUnixTimeMillis();
            for (int looptest = 0; looptest < nbTest; looptest++)
            {
                ValueDerivatives computed = MODEL.alphaAdjoint(MODEL_PARAMETERS, expiry1, expiry2, numeraire, maturity);
                alpha = computed.Value;
            }
            endTime = DateTimeHelper.CurrentUnixTimeMillis();
            Console.WriteLine(nbTest + " alpha Hull-White adjoint (value+" + nbVolatility + " derivatives): " + (endTime - startTime) + " ms");
            // Performance note: value: 31-Aug-11: On Mac Pro 3.2 GHz Quad-Core Intel Xeon: 75 ms for 1000000 swaptions.
            // Performance note: value+derivatives: 31-Aug-11: On Mac Pro 3.2 GHz Quad-Core Intel Xeon: 100 ms for 1000000 swaptions.
            Console.WriteLine("Alpha: " + alpha);
        }
예제 #13
0
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the present value sensitivity of the Ibor caplet/floorlet to the rate curves.
        /// <para>
        /// The present value sensitivity is computed in a "sticky model parameter" style, i.e. the sensitivity to the
        /// curve nodes with the SABR model parameters unchanged. This sensitivity does not include a potential
        /// re-calibration of the model parameters to the raw market data.
        ///
        /// </para>
        /// </summary>
        /// <param name="period">  the Ibor caplet/floorlet period </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the volatilities </param>
        /// <returns> the point sensitivity to the rate curves </returns>
        public virtual PointSensitivityBuilder presentValueSensitivityRatesStickyModel(IborCapletFloorletPeriod period, RatesProvider ratesProvider, SabrIborCapletFloorletVolatilities volatilities)
        {
            Currency currency = period.Currency;

            if (ratesProvider.ValuationDate.isAfter(period.PaymentDate))
            {
                return(PointSensitivityBuilder.none());
            }
            double  expiry    = volatilities.relativeTime(period.FixingDateTime);
            PutCall putCall   = period.PutCall;
            double  strike    = period.Strike;
            double  indexRate = ratesProvider.iborIndexRates(period.Index).rate(period.IborRate.Observation);
            PointSensitivityBuilder dfSensi = ratesProvider.discountFactors(currency).zeroRatePointSensitivity(period.PaymentDate);
            double factor = period.Notional * period.YearFraction;

            if (expiry < 0d)
            {     // option expired already, but not yet paid
                double sign   = putCall.Call ? 1d : -1d;
                double payoff = Math.Max(sign * (indexRate - strike), 0d);
                return(dfSensi.multipliedBy(payoff * factor));
            }
            ValueDerivatives        volatilityAdj       = volatilities.volatilityAdjoint(expiry, strike, indexRate);
            PointSensitivityBuilder indexRateSensiSensi = ratesProvider.iborIndexRates(period.Index).ratePointSensitivity(period.IborRate.Observation);
            double df       = ratesProvider.discountFactor(currency, period.PaymentDate);
            double fwdPv    = factor * volatilities.price(expiry, putCall, strike, indexRate, volatilityAdj.Value);
            double fwdDelta = factor * volatilities.priceDelta(expiry, putCall, strike, indexRate, volatilityAdj.Value);
            double fwdVega  = factor * volatilities.priceVega(expiry, putCall, strike, indexRate, volatilityAdj.Value);

            return(dfSensi.multipliedBy(fwdPv).combinedWith(indexRateSensiSensi.multipliedBy(fwdDelta * df + fwdVega * volatilityAdj.getDerivative(0) * df)));
        }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the present value sensitivity of the swaption to the rate curves.
        /// <para>
        /// The present value sensitivity is computed in a "sticky strike" style, i.e. the sensitivity to the
        /// curve nodes with the volatility at the swaption strike unchanged. This sensitivity does not include a potential
        /// change of volatility due to the implicit change of forward rate or moneyness.
        ///
        /// </para>
        /// </summary>
        /// <param name="swaption">  the swaption </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="swaptionVolatilities">  the volatilities </param>
        /// <returns> the point sensitivity to the rate curves </returns>
        public virtual PointSensitivityBuilder presentValueSensitivityRatesStickyStrike(ResolvedSwaption swaption, RatesProvider ratesProvider, SwaptionVolatilities swaptionVolatilities)
        {
            validate(swaption, ratesProvider, swaptionVolatilities);
            double          expiry     = swaptionVolatilities.relativeTime(swaption.Expiry);
            ResolvedSwap    underlying = swaption.Underlying;
            ResolvedSwapLeg fixedLeg   = this.fixedLeg(underlying);

            if (expiry < 0d)
            {     // Option has expired already
                return(PointSensitivityBuilder.none());
            }
            double           forward           = SwapPricer.parRate(underlying, ratesProvider);
            ValueDerivatives annuityDerivative = SwapPricer.LegPricer.annuityCashDerivative(fixedLeg, forward);
            double           annuityCash       = annuityDerivative.Value;
            double           annuityCashDr     = annuityDerivative.getDerivative(0);
            LocalDate        settlementDate    = ((CashSwaptionSettlement)swaption.SwaptionSettlement).SettlementDate;
            double           discountSettle    = ratesProvider.discountFactor(fixedLeg.Currency, settlementDate);
            double           strike            = calculateStrike(fixedLeg);
            double           tenor             = swaptionVolatilities.tenor(fixedLeg.StartDate, fixedLeg.EndDate);
            double           volatility        = swaptionVolatilities.volatility(expiry, tenor, strike, forward);
            PutCall          putCall           = PutCall.ofPut(fixedLeg.PayReceive.Receive);
            double           price             = swaptionVolatilities.price(expiry, tenor, putCall, strike, forward, volatility);
            double           delta             = swaptionVolatilities.priceDelta(expiry, tenor, putCall, strike, forward, volatility);
            // Backward sweep
            PointSensitivityBuilder forwardSensi        = SwapPricer.parRateSensitivity(underlying, ratesProvider);
            PointSensitivityBuilder discountSettleSensi = ratesProvider.discountFactors(fixedLeg.Currency).zeroRatePointSensitivity(settlementDate);
            double sign = swaption.LongShort.sign();

            return(forwardSensi.multipliedBy(sign * discountSettle * (annuityCash * delta + annuityCashDr * price)).combinedWith(discountSettleSensi.multipliedBy(sign * annuityCash * price)));
        }
        /// <summary>
        /// Computes the option price derivative with respect to the forward.
        /// <para>
        /// The price is SABR below the cut-off strike and extrapolated beyond.
        ///
        /// </para>
        /// </summary>
        /// <param name="strike">  the strike of the option </param>
        /// <param name="putCall">  whether the option is put or call </param>
        /// <returns> the option price derivative </returns>
        public double priceDerivativeForward(double strike, PutCall putCall)
        {
            // Uses Hagan et al SABR function.
            if (strike <= cutOffStrike)
            {
                ValueDerivatives volatilityA = sabrFunction.volatilityAdjoint(forward, strike, timeToExpiry, sabrData);
                ValueDerivatives pA          = BlackFormulaRepository.priceAdjoint(forward, strike, timeToExpiry, volatilityA.Value, putCall == PutCall.CALL);
                return(pA.getDerivative(0) + pA.getDerivative(3) * volatilityA.getDerivative(0));
            }
            // Uses extrapolation for call.
            if (parameterDerivativeForward == null)
            {
                parameterDerivativeForward = computesParametersDerivativeForward();
            }
            double f               = extrapolation(strike);
            double fDa             = f;
            double fDb             = f / strike;
            double fDc             = fDb / strike;
            double priceDerivative = fDa * parameterDerivativeForward[0] + fDb * parameterDerivativeForward[1] + fDc * parameterDerivativeForward[2];

            if (putCall.Put)
            {     // Put by call/put parity
                priceDerivative -= 1;
            }
            return(priceDerivative);
        }
        /// <summary>
        /// Calculates the future convexity factor and its derivatives with respect to the model volatilities.
        /// <para>
        /// The factor is called gamma in the reference:
        /// Henrard, M. "The Irony in the derivatives discounting Part II: the crisis", Wilmott Journal, 2010, 2, 301-316
        ///
        /// </para>
        /// </summary>
        /// <param name="data">  the Hull-White model parameters </param>
        /// <param name="t0">  the expiry time </param>
        /// <param name="t1">  the first reference time </param>
        /// <param name="t2">  the second reference time </param>
        /// <returns> the factor and drivatives </returns>
        public ValueDerivatives futuresConvexityFactorAdjoint(HullWhiteOneFactorPiecewiseConstantParameters data, double t0, double t1, double t2)
        {
            double factor1   = Math.Exp(-data.MeanReversion * t1) - Math.Exp(-data.MeanReversion * t2);
            double numerator = 2 * data.MeanReversion * data.MeanReversion * data.MeanReversion;
            int    indexT0   = 1; // Period in which the time t0 is; volatilityTime[i-1] <= t0 < volatilityTime[i];

            while (t0 > data.VolatilityTime.get(indexT0))
            {
                indexT0++;
            }
            double[] s = new double[indexT0 + 1];
            Array.Copy(data.VolatilityTime.toArray(), 0, s, 0, indexT0);
            s[indexT0] = t0;
            double factor2 = 0.0;

            double[] factorExp = new double[indexT0];
            for (int loopperiod = 0; loopperiod < indexT0; loopperiod++)
            {
                factorExp[loopperiod] = (Math.Exp(data.MeanReversion * s[loopperiod + 1]) - Math.Exp(data.MeanReversion * s[loopperiod])) * (2 - Math.Exp(-data.MeanReversion * (t2 - s[loopperiod + 1])) - Math.Exp(-data.MeanReversion * (t2 - s[loopperiod])));
                factor2 += data.Volatility.get(loopperiod) * data.Volatility.get(loopperiod) * factorExp[loopperiod];
            }
            double factor = Math.Exp(factor1 / numerator * factor2);
            // Backward sweep
            double factorBar  = 1.0;
            double factor2Bar = factor1 / numerator * factor * factorBar;

            double[] derivatives = new double[data.Volatility.size()];
            for (int loopperiod = 0; loopperiod < indexT0; loopperiod++)
            {
                derivatives[loopperiod] = 2 * data.Volatility.get(loopperiod) * factorExp[loopperiod] * factor2Bar;
            }
            return(ValueDerivatives.of(factor, DoubleArray.ofUnsafe(derivatives)));
        }
        /// <summary>
        /// Calculates the first order derivative of the swap rate with respect to the {@code alphaFixed}
        /// in the {@code P(*,theta)} numeraire.
        /// </summary>
        /// <param name="x">  the random variable value. </param>
        /// <param name="discountedCashFlowFixed">  the discounted cash flows equivalent of the swap fixed leg </param>
        /// <param name="alphaFixed">  the zero-coupon bond volatilities for the swap fixed leg </param>
        /// <param name="discountedCashFlowIbor">  the discounted cash flows equivalent of the swap Ibor leg </param>
        /// <param name="alphaIbor">  the zero-coupon bond volatilities for the swap Ibor leg </param>
        /// <returns> the swap rate and derivatives </returns>
        public ValueDerivatives swapRateDaf1(double x, DoubleArray discountedCashFlowFixed, DoubleArray alphaFixed, DoubleArray discountedCashFlowIbor, DoubleArray alphaIbor)
        {
            int sizeIbor  = discountedCashFlowIbor.size();
            int sizeFixed = discountedCashFlowFixed.size();

            ArgChecker.isTrue(sizeIbor == alphaIbor.size(), "Length should be equal");
            ArgChecker.isTrue(sizeFixed == alphaFixed.size(), "Length should be equal");
            double[] expD      = new double[sizeIbor];
            double   numerator = 0.0;

            for (int loopcf = 0; loopcf < sizeIbor; loopcf++)
            {
                numerator += discountedCashFlowIbor.get(loopcf) * Math.Exp(-alphaIbor.get(loopcf) * x - 0.5 * alphaIbor.get(loopcf) * alphaIbor.get(loopcf));
            }
            double denominator = 0.0;

            for (int loopcf = 0; loopcf < sizeFixed; loopcf++)
            {
                expD[loopcf] = discountedCashFlowFixed.get(loopcf) * Math.Exp(-alphaFixed.get(loopcf) * x - 0.5 * alphaFixed.get(loopcf) * alphaFixed.get(loopcf));
                denominator += expD[loopcf];
            }
            double ratio = numerator / (denominator * denominator);

            double[] swapRateDaf1 = new double[sizeFixed];
            for (int loopcf = 0; loopcf < sizeFixed; loopcf++)
            {
                swapRateDaf1[loopcf] = ratio * expD[loopcf] * (-x - alphaFixed.get(loopcf));
            }
            return(ValueDerivatives.of(-numerator / denominator, DoubleArray.ofUnsafe(swapRateDaf1)));
        }
        /// <summary>
        /// Test the adjoint algorithmic differentiation version of alpha.
        /// </summary>
        public virtual void alphaDSigma()
        {
            double           expiry1      = 0.25;
            double           expiry2      = 2.25;
            double           numeraire    = 10.0;
            double           maturity     = 9.0;
            int              nbVolatility = VOLATILITY.size();
            ValueDerivatives alphaDeriv   = MODEL.alphaAdjoint(MODEL_PARAMETERS, expiry1, expiry2, numeraire, maturity);
            double           alpha        = alphaDeriv.Value;

            double[] alphaDerivatives = alphaDeriv.Derivatives.toArray();
            double   alpha2           = MODEL.alpha(MODEL_PARAMETERS, expiry1, expiry2, numeraire, maturity);

            assertEquals(alpha2, alpha, 1.0E-10);
            double shiftVol = 1.0E-6;

            double[] volatilityBumped = new double[nbVolatility];
            Array.Copy(VOLATILITY.toArray(), 0, volatilityBumped, 0, nbVolatility);
            double[] alphaBumpedPlus  = new double[nbVolatility];
            double[] alphaBumpedMinus = new double[nbVolatility];
            HullWhiteOneFactorPiecewiseConstantParameters parametersBumped;

            for (int loopvol = 0; loopvol < nbVolatility; loopvol++)
            {
                volatilityBumped[loopvol] += shiftVol;
                parametersBumped           = HullWhiteOneFactorPiecewiseConstantParameters.of(MEAN_REVERSION, DoubleArray.copyOf(volatilityBumped), VOLATILITY_TIME);
                alphaBumpedPlus[loopvol]   = MODEL.alpha(parametersBumped, expiry1, expiry2, numeraire, maturity);
                volatilityBumped[loopvol] -= 2 * shiftVol;
                parametersBumped           = HullWhiteOneFactorPiecewiseConstantParameters.of(MEAN_REVERSION, DoubleArray.copyOf(volatilityBumped), VOLATILITY_TIME);
                alphaBumpedMinus[loopvol]  = MODEL.alpha(parametersBumped, expiry1, expiry2, numeraire, maturity);
                assertEquals((alphaBumpedPlus[loopvol] - alphaBumpedMinus[loopvol]) / (2 * shiftVol), alphaDerivatives[loopvol], 1.0E-9);
                volatilityBumped[loopvol] = VOLATILITY.get(loopvol);
            }
        }
        //-------------------------------------------------------------------------
        private ValueDerivatives priceDerivatives(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities, RecombiningTrinomialTreeData data)
        {
            validate(option, ratesProvider, volatilities);
            validateData(option, ratesProvider, volatilities, data);
            int nSteps = data.NumberOfSteps;
            ResolvedFxVanillaOption underlyingOption = option.UnderlyingOption;
            double           timeToExpiry            = data.getTime(nSteps);
            ResolvedFxSingle underlyingFx            = underlyingOption.Underlying;
            Currency         ccyBase                  = underlyingFx.CounterCurrencyPayment.Currency;
            Currency         ccyCounter               = underlyingFx.CounterCurrencyPayment.Currency;
            DiscountFactors  baseDiscountFactors      = ratesProvider.discountFactors(ccyBase);
            DiscountFactors  counterDiscountFactors   = ratesProvider.discountFactors(ccyCounter);
            double           rebateAtExpiry           = 0d; // used to price knock-in option
            double           rebateAtExpiryDerivative = 0d; // used to price knock-in option
            double           notional                 = Math.Abs(underlyingFx.BaseCurrencyPayment.Amount);

            double[] rebateArray = new double[nSteps + 1];
            SimpleConstantContinuousBarrier barrier = (SimpleConstantContinuousBarrier)option.Barrier;

            if (option.Rebate.Present)
            {
                CurrencyAmount rebateCurrencyAmount = option.Rebate.get();
                double         rebatePerUnit        = rebateCurrencyAmount.Amount / notional;
                bool           isCounter            = rebateCurrencyAmount.Currency.Equals(ccyCounter);
                double         rebate = isCounter ? rebatePerUnit : rebatePerUnit * barrier.BarrierLevel;
                if (barrier.KnockType.KnockIn)
                {   // use in-out parity
                    double dfCounterAtExpiry = counterDiscountFactors.discountFactor(timeToExpiry);
                    double dfBaseAtExpiry    = baseDiscountFactors.discountFactor(timeToExpiry);
                    for (int i = 0; i < nSteps + 1; ++i)
                    {
                        rebateArray[i] = isCounter ? rebate * dfCounterAtExpiry / counterDiscountFactors.discountFactor(data.getTime(i)) : rebate * dfBaseAtExpiry / baseDiscountFactors.discountFactor(data.getTime(i));
                    }
                    if (isCounter)
                    {
                        rebateAtExpiry = rebatePerUnit * dfCounterAtExpiry;
                    }
                    else
                    {
                        rebateAtExpiry           = rebatePerUnit * data.Spot * dfBaseAtExpiry;
                        rebateAtExpiryDerivative = rebatePerUnit * dfBaseAtExpiry;
                    }
                }
                else
                {
                    Arrays.fill(rebateArray, rebate);
                }
            }
            ConstantContinuousSingleBarrierKnockoutFunction barrierFunction = ConstantContinuousSingleBarrierKnockoutFunction.of(underlyingOption.Strike, timeToExpiry, underlyingOption.PutCall, nSteps, barrier.BarrierType, barrier.BarrierLevel, DoubleArray.ofUnsafe(rebateArray));
            ValueDerivatives barrierPrice = TREE.optionPriceAdjoint(barrierFunction, data);

            if (barrier.KnockType.KnockIn)
            {     // use in-out parity
                EuropeanVanillaOptionFunction vanillaFunction = EuropeanVanillaOptionFunction.of(underlyingOption.Strike, timeToExpiry, underlyingOption.PutCall, nSteps);
                ValueDerivatives vanillaPrice = TREE.optionPriceAdjoint(vanillaFunction, data);
                return(ValueDerivatives.of(vanillaPrice.Value + rebateAtExpiry - barrierPrice.Value, DoubleArray.of(vanillaPrice.getDerivative(0) + rebateAtExpiryDerivative - barrierPrice.getDerivative(0))));
            }
            return(barrierPrice);
        }
 private double[] toArray(ValueDerivatives valueDerivatives)
 {
     double[] derivatives = valueDerivatives.Derivatives.toArray();
     double[] res         = new double[derivatives.Length + 1];
     res[0] = valueDerivatives.Value;
     Array.Copy(derivatives, 0, res, 1, derivatives.Length);
     return(res);
 }
        /// <summary>
        /// Calculates the delta of the FX barrier option product.
        /// <para>
        /// The delta is the first derivative of <seealso cref="#price"/> with respect to spot.
        ///
        /// </para>
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <returns> the delta of the product </returns>
        public virtual double delta(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities)
        {
            if (volatilities.relativeTime(option.UnderlyingOption.Expiry) < 0d)
            {
                return(0d);
            }
            ValueDerivatives priceDerivatives = this.priceDerivatives(option, ratesProvider, volatilities);

            return(priceDerivatives.getDerivative(0));
        }
예제 #22
0
        /// <summary>
        /// Computes the derivative of the conventional cash annuity with respect to the yield from a swap leg.
        /// <para>
        /// The computation is relevant only for standard swaps with constant notional and regular payments.
        /// The swap leg must be a fixed leg. However, this is not checked internally.
        ///
        /// </para>
        /// </summary>
        /// <param name="fixedLeg">  the fixed leg of the swap </param>
        /// <param name="yield">  the yield </param>
        /// <returns> the cash annuity </returns>
        public virtual ValueDerivatives annuityCashDerivative(ResolvedSwapLeg fixedLeg, double yield)
        {
            int nbFixedPeriod = fixedLeg.PaymentPeriods.size();
            SwapPaymentPeriod paymentPeriod = fixedLeg.PaymentPeriods.get(0);

            ArgChecker.isTrue(paymentPeriod is RatePaymentPeriod, "payment period should be RatePaymentPeriod");
            RatePaymentPeriod ratePaymentPeriod = (RatePaymentPeriod)paymentPeriod;
            int              nbFixedPaymentYear = (int)(long)Math.Round(1d / ratePaymentPeriod.DayCount.yearFraction(ratePaymentPeriod.StartDate, ratePaymentPeriod.EndDate), MidpointRounding.AwayFromZero);
            double           notional           = Math.Abs(ratePaymentPeriod.Notional);
            ValueDerivatives annuityUnit        = annuityCash1(nbFixedPaymentYear, nbFixedPeriod, yield);

            return(ValueDerivatives.of(annuityUnit.Value * notional, annuityUnit.Derivatives.multipliedBy(notional)));
        }
예제 #23
0
        /// <summary>
        /// Calculates volatility and the adjoint (volatility sensitivity to forward, strike and model parameters).
        /// <para>
        /// By default the derivatives are computed by central finite difference approximation.
        /// This should be overridden in each subclass.
        ///
        /// </para>
        /// </summary>
        /// <param name="forward">  the forward value of the underlying </param>
        /// <param name="strike">  the strike value of the option </param>
        /// <param name="timeToExpiry">  the time to expiry of the option </param>
        /// <param name="data">  the model data </param>
        /// <returns> the volatility and associated derivatives </returns>
        public virtual ValueDerivatives volatilityAdjoint(double forward, double strike, double timeToExpiry, T data)
        {
            ArgChecker.isTrue(forward >= 0.0, "forward must be greater than zero");

            double[] res        = new double[2 + data.NumberOfParameters]; // fwd, strike, the model parameters
            double   volatility = this.volatility(forward, strike, timeToExpiry, data);

            res[0] = forwardBar(forward, strike, timeToExpiry, data);
            res[1] = strikeBar(forward, strike, timeToExpiry, data);
            System.Func <T, double> func = getVolatilityFunction(forward, strike, timeToExpiry);
            double[] modelAdjoint        = paramBar(func, data);
            Array.Copy(modelAdjoint, 0, res, 2, data.NumberOfParameters);
            return(ValueDerivatives.of(volatility, DoubleArray.ofUnsafe(res)));
        }
        /// <summary>
        /// Calculates the currency exposure of the FX barrier option product.
        /// <para>
        /// This assumes the tree is already calibrated and the tree data is stored as {@code RecombiningTrinomialTreeData}.
        /// The tree data should be consistent with the pricer and other inputs, see <seealso cref="#validateData"/>.
        ///
        /// </para>
        /// </summary>
        /// <param name="option">  the option product </param>
        /// <param name="ratesProvider">  the rates provider </param>
        /// <param name="volatilities">  the Black volatility provider </param>
        /// <param name="treeData">  the trinomial tree data </param>
        /// <returns> the currency exposure </returns>
        public virtual MultiCurrencyAmount currencyExposure(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities, RecombiningTrinomialTreeData treeData)
        {
            ResolvedFxVanillaOption underlyingOption = option.UnderlyingOption;
            ValueDerivatives        priceDerivatives = this.priceDerivatives(option, ratesProvider, volatilities, treeData);
            double         price          = priceDerivatives.Value;
            double         delta          = priceDerivatives.getDerivative(0);
            CurrencyPair   currencyPair   = underlyingOption.Underlying.CurrencyPair;
            double         todayFx        = ratesProvider.fxRate(currencyPair);
            double         signedNotional = this.signedNotional(underlyingOption);
            CurrencyAmount domestic       = CurrencyAmount.of(currencyPair.Counter, (price - delta * todayFx) * signedNotional);
            CurrencyAmount foreign        = CurrencyAmount.of(currencyPair.Base, delta * signedNotional);

            return(MultiCurrencyAmount.of(domestic, foreign));
        }
예제 #25
0
        /// <summary>
        /// Test consistency between price methods, and Greek via finite difference.
        /// </summary>
        public virtual void test_trinomialTree()
        {
            int    nSteps = 135;
            double dt     = TIME / nSteps;
            LatticeSpecification lattice = new CoxRossRubinsteinLatticeSpecification();
            double fdEps = 1.0e-4;

            foreach (bool isCall in new bool[] { true, false })
            {
                foreach (double strike in STRIKES)
                {
                    foreach (double interest in INTERESTS)
                    {
                        foreach (double vol in VOLS)
                        {
                            foreach (double dividend in DIVIDENDS)
                            {
                                OptionFunction function   = EuropeanVanillaOptionFunction.of(strike, TIME, PutCall.ofPut(!isCall), nSteps);
                                double[]       @params    = lattice.getParametersTrinomial(vol, interest - dividend, dt).toArray();
                                DoubleArray    time       = DoubleArray.of(nSteps + 1, i => dt * i);
                                DoubleArray    df         = DoubleArray.of(nSteps, i => Math.Exp(-interest * dt));
                                double[][]     stateValue = new double[nSteps + 1][];
                                stateValue[0] = new double[] { SPOT };
                                IList <DoubleMatrix> prob  = new List <DoubleMatrix>();
                                double[]             probs = new double[] { @params[5], @params[4], @params[3] };
                                for (int i = 0; i < nSteps; ++i)
                                {
                                    int index = i;
                                    stateValue[i + 1] = DoubleArray.of(2 * i + 3, j => SPOT * Math.Pow(@params[2], index + 1 - j) * Math.Pow(@params[1], j)).toArray();
                                    double[][] probMatrix = new double[2 * i + 1][];
                                    Arrays.fill(probMatrix, probs);
                                    prob.Add(DoubleMatrix.ofUnsafe(probMatrix));
                                }
                                RecombiningTrinomialTreeData treeData = RecombiningTrinomialTreeData.of(DoubleMatrix.ofUnsafe(stateValue), prob, df, time);
                                double priceData   = TRINOMIAL_TREE.optionPrice(function, treeData);
                                double priceParams = TRINOMIAL_TREE.optionPrice(function, lattice, SPOT, vol, interest, dividend);
                                assertEquals(priceData, priceParams);
                                ValueDerivatives priceDeriv = TRINOMIAL_TREE.optionPriceAdjoint(function, treeData);
                                assertEquals(priceDeriv.Value, priceData);
                                double priceUp = TRINOMIAL_TREE.optionPrice(function, lattice, SPOT + fdEps, vol, interest, dividend);
                                double priceDw = TRINOMIAL_TREE.optionPrice(function, lattice, SPOT - fdEps, vol, interest, dividend);
                                double fdDelta = 0.5 * (priceUp - priceDw) / fdEps;
                                assertEquals(priceDeriv.getDerivative(0), fdDelta, 3.0e-2);
                            }
                        }
                    }
                }
            }
        }
예제 #26
0
        //-------------------------------------------------------------------------
        /// <summary>
        /// Computes the implied volatility.
        /// <para>
        /// If the volatility data is not zero, it is used as a starting point for the volatility search.
        /// </para>
        /// <para>
        /// Note that the 'numeraire' is a simple multiplier and is the responsibility of the caller.
        ///
        /// </para>
        /// </summary>
        /// <param name="optionPrice">  the price of the option </param>
        /// <param name="forward">  the forward value of the underlying </param>
        /// <param name="strike">  the strike </param>
        /// <param name="timeToExpiry">  the time to expiry </param>
        /// <param name="initialNormalVol">  the normal volatility used to start the search </param>
        /// <param name="numeraire">  the numeraire </param>
        /// <param name="putCall">  whether it is put or call </param>
        /// <returns> the implied volatility </returns>
        public static double impliedVolatility(double optionPrice, double forward, double strike, double timeToExpiry, double initialNormalVol, double numeraire, PutCall putCall)
        {
            double intrinsicPrice = numeraire * Math.Max(0, (putCall.Call ? 1 : -1) * (forward - strike));

            ArgChecker.isTrue(optionPrice > intrinsicPrice || DoubleMath.fuzzyEquals(optionPrice, intrinsicPrice, 1e-6), "Option price (" + optionPrice + ") less than intrinsic value (" + intrinsicPrice + ")");
            if (System.BitConverter.DoubleToInt64Bits(optionPrice) == Double.doubleToLongBits(intrinsicPrice))
            {
                return(0d);
            }
            double           sigma     = (Math.Abs(initialNormalVol) < 1e-10 ? 0.3 * forward : initialNormalVol);
            double           maxChange = 0.5 * forward;
            ValueDerivatives price     = priceAdjoint(forward, strike, timeToExpiry, sigma, numeraire, putCall);
            double           vega      = price.getDerivative(1);
            double           change    = (price.Value - optionPrice) / vega;
            double           sign      = Math.Sign(change);

            change = sign * Math.Min(maxChange, Math.Abs(change));
            if (change > 0 && change > sigma)
            {
                change = sigma;
            }
            int count = 0;

            while (Math.Abs(change) > EPS)
            {
                sigma -= change;
                price  = priceAdjoint(forward, strike, timeToExpiry, sigma, numeraire, putCall);
                vega   = price.getDerivative(1);
                change = (price.Value - optionPrice) / vega;
                sign   = Math.Sign(change);
                change = sign * Math.Min(maxChange, Math.Abs(change));
                if (change > 0 && change > sigma)
                {
                    change = sigma;
                }
                if (count++ > MAX_ITERATIONS)
                {
                    BracketRoot bracketer = new BracketRoot();
                    BisectionSingleRootFinder    rootFinder = new BisectionSingleRootFinder(EPS);
                    System.Func <double, double> func       = (double?volatility) =>
                    {
                        return(numeraire * NormalFormulaRepository.price(forward, strike, timeToExpiry, volatility.Value, putCall) - optionPrice);
                    };
                    double[] range = bracketer.getBracketedPoints(func, 0d, 10d);
                    return(rootFinder.getRoot(func, range[0], range[1]).Value);
                }
            }
            return(sigma);
        }
예제 #27
0
        //-------------------------------------------------------------------------
        /// <summary>
        /// Computes the price and first order derivatives.
        /// <para>
        /// The derivatives are stored in an array with:
        /// <ul>
        /// <li>[0] derivative with respect to the forward
        /// <li>[1] derivative with respect to the volatility
        /// <li>[2] derivative with respect to the strike
        /// </ul>
        ///
        /// </para>
        /// </summary>
        /// <param name="forward">  the forward value of the underlying </param>
        /// <param name="strike">  the strike </param>
        /// <param name="timeToExpiry">  the time to expiry </param>
        /// <param name="normalVol">  the normal volatility </param>
        /// <param name="numeraire">  the numeraire </param>
        /// <param name="putCall">  whether it is put or call </param>
        /// <returns> the price and associated derivatives </returns>
        public static ValueDerivatives priceAdjoint(double forward, double strike, double timeToExpiry, double normalVol, double numeraire, PutCall putCall)
        {
            int    sign = putCall.Call ? 1 : -1;
            double price;
            double cdf = 0d;
            double pdf = 0d;
            double arg = 0d;
            double x   = 0d;
            // Implementation Note: Forward sweep.
            double sigmaRootT = normalVol * Math.Sqrt(timeToExpiry);

            if (sigmaRootT < NormalFormulaRepository.NEAR_ZERO)
            {
                x     = sign * (forward - strike);
                price = (x > 0 ? numeraire * x : 0d);
            }
            else
            {
                arg   = sign * (forward - strike) / sigmaRootT;
                cdf   = NormalFormulaRepository.DISTRIBUTION.getCDF(arg);
                pdf   = NormalFormulaRepository.DISTRIBUTION.getPDF(arg);
                price = numeraire * (sign * (forward - strike) * cdf + sigmaRootT * pdf);
            }
            // Implementation Note: Backward sweep.
            double forwardDerivative;
            double volatilityDerivative;
            double strikeDerivative;
            double priceBar = 1d;

            if (sigmaRootT < NormalFormulaRepository.NEAR_ZERO)
            {
                double xBar = (x > 0 ? numeraire : 0d);
                forwardDerivative    = sign * xBar;
                strikeDerivative     = -forwardDerivative;
                volatilityDerivative = 0d;
            }
            else
            {
                double cdfBar = numeraire * (sign * (forward - strike)) * priceBar;
                double pdfBar = numeraire * sigmaRootT * priceBar;
                double argBar = pdf * cdfBar - pdf * arg * pdfBar;
                forwardDerivative = numeraire * sign * cdf * priceBar + sign / sigmaRootT * argBar;
                strikeDerivative  = -forwardDerivative;
                double sigmaRootTBar = -arg / sigmaRootT * argBar + numeraire * pdf * priceBar;
                volatilityDerivative = Math.Sqrt(timeToExpiry) * sigmaRootTBar;
            }
            return(ValueDerivatives.of(price, DoubleArray.of(forwardDerivative, volatilityDerivative, strikeDerivative)));
        }
        //-------------------------------------------------------------------------
        //  The derivatives are [0] spot, [1] strike, [2] rate, [3] cost-of-carry, [4] volatility, [5] timeToExpiry, [6] spot twice
        private ValueDerivatives priceDerivatives(ResolvedFxSingleBarrierOption option, RatesProvider ratesProvider, BlackFxOptionVolatilities volatilities)
        {
            validate(option, ratesProvider, volatilities);
            SimpleConstantContinuousBarrier barrier          = (SimpleConstantContinuousBarrier)option.Barrier;
            ResolvedFxVanillaOption         underlyingOption = option.UnderlyingOption;

            double[] derivatives = new double[7];
            if (volatilities.relativeTime(underlyingOption.Expiry) < 0d)
            {
                return(ValueDerivatives.of(0d, DoubleArray.ofUnsafe(derivatives)));
            }
            ResolvedFxSingle underlyingFx           = underlyingOption.Underlying;
            CurrencyPair     currencyPair           = underlyingFx.CurrencyPair;
            Currency         ccyBase                = currencyPair.Base;
            Currency         ccyCounter             = currencyPair.Counter;
            DiscountFactors  baseDiscountFactors    = ratesProvider.discountFactors(ccyBase);
            DiscountFactors  counterDiscountFactors = ratesProvider.discountFactors(ccyCounter);

            double           rateBase         = baseDiscountFactors.zeroRate(underlyingFx.PaymentDate);
            double           rateCounter      = counterDiscountFactors.zeroRate(underlyingFx.PaymentDate);
            double           costOfCarry      = rateCounter - rateBase;
            double           dfBase           = baseDiscountFactors.discountFactor(underlyingFx.PaymentDate);
            double           dfCounter        = counterDiscountFactors.discountFactor(underlyingFx.PaymentDate);
            double           todayFx          = ratesProvider.fxRate(currencyPair);
            double           strike           = underlyingOption.Strike;
            double           forward          = todayFx * dfBase / dfCounter;
            double           volatility       = volatilities.volatility(currencyPair, underlyingOption.Expiry, strike, forward);
            double           timeToExpiry     = volatilities.relativeTime(underlyingOption.Expiry);
            ValueDerivatives valueDerivatives = BARRIER_PRICER.priceAdjoint(todayFx, strike, timeToExpiry, costOfCarry, rateCounter, volatility, underlyingOption.PutCall.Call, barrier);

            if (!option.Rebate.Present)
            {
                return(valueDerivatives);
            }
            CurrencyAmount   rebate = option.Rebate.get();
            ValueDerivatives valueDerivativesRebate = rebate.Currency.Equals(ccyCounter) ? CASH_REBATE_PRICER.priceAdjoint(todayFx, timeToExpiry, costOfCarry, rateCounter, volatility, barrier.inverseKnockType()) : ASSET_REBATE_PRICER.priceAdjoint(todayFx, timeToExpiry, costOfCarry, rateCounter, volatility, barrier.inverseKnockType());
            double           rebateRate             = rebate.Amount / Math.Abs(underlyingFx.BaseCurrencyPayment.Amount);
            double           price = valueDerivatives.Value + rebateRate * valueDerivativesRebate.Value;

            derivatives[0] = valueDerivatives.getDerivative(0) + rebateRate * valueDerivativesRebate.getDerivative(0);
            derivatives[1] = valueDerivatives.getDerivative(1);
            for (int i = 2; i < 7; ++i)
            {
                derivatives[i] = valueDerivatives.getDerivative(i) + rebateRate * valueDerivativesRebate.getDerivative(i - 1);
            }
            return(ValueDerivatives.of(price, DoubleArray.ofUnsafe(derivatives)));
        }
        /// <summary>
        /// Calculate the true SABR delta and gamma and compare with that found by finite difference
        /// </summary>
//JAVA TO C# CONVERTER TODO TASK: Most Java annotations will not have direct .NET equivalent attributes:
//ORIGINAL LINE: @Test(enabled = false) public void testGreeks()
        public virtual void testGreeks()
        {
            double           eps             = 1e-3;
            double           f               = 1.2;
            double           k               = 1.4;
            double           t               = 5.0;
            double           alpha           = 0.3;
            double           beta            = 0.6;
            double           rho             = -0.4;
            double           nu              = 0.4;
            SabrFormulaData  sabrData        = SabrFormulaData.of(alpha, beta, rho, nu);
            ValueDerivatives adj             = FUNCTION.volatilityAdjoint(f, k, t, sabrData);
            double           bsDelta         = BlackFormulaRepository.delta(f, k, t, adj.Value, true);
            double           bsVega          = BlackFormulaRepository.vega(f, k, t, adj.Value);
            double           volForwardSense = adj.getDerivative(1);
            double           delta           = bsDelta + bsVega * volForwardSense;

            SabrFormulaData data      = SabrFormulaData.of(alpha, beta, rho, nu);
            double          volUp     = FUNCTION.volatility(f + eps, k, t, data);
            double          volDown   = FUNCTION.volatility(f - eps, k, t, data);
            double          priceUp   = BlackFormulaRepository.price(f + eps, k, t, volUp, true);
            double          price     = BlackFormulaRepository.price(f, k, t, adj.Value, true);
            double          priceDown = BlackFormulaRepository.price(f - eps, k, t, volDown, true);
            double          fdDelta   = (priceUp - priceDown) / 2 / eps;

            assertEquals(fdDelta, delta, 1e-6);

            double bsVanna = BlackFormulaRepository.vanna(f, k, t, adj.Value);
            double bsGamma = BlackFormulaRepository.gamma(f, k, t, adj.Value);

            double[] volD1 = new double[5];
//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[][] volD2 = new double[2][2];
            double[][] volD2 = RectangularArrays.ReturnRectangularDoubleArray(2, 2);
            FUNCTION.volatilityAdjoint2(f, k, t, sabrData, volD1, volD2);
            double d2Sigmad2Fwd = volD2[0][0];
            double gamma        = bsGamma + 2 * bsVanna * adj.getDerivative(1) + bsVega * d2Sigmad2Fwd;
            double fdGamma      = (priceUp + priceDown - 2 * price) / eps / eps;

            double d2Sigmad2FwdFD = (volUp + volDown - 2 * adj.Value) / eps / eps;

            assertEquals(d2Sigmad2FwdFD, d2Sigmad2Fwd, 1e-4);

            assertEquals(fdGamma, gamma, 1e-2);
        }
        /// <summary>
        /// Computes the option price derivative with respect to the strike.
        /// <para>
        /// The price is SABR below the cut-off strike and extrapolated beyond.
        ///
        /// </para>
        /// </summary>
        /// <param name="strike">  the strike of the option </param>
        /// <param name="putCall">  whether the option is put or call </param>
        /// <returns> the option price derivative </returns>
        public double priceDerivativeStrike(double strike, PutCall putCall)
        {
            // Uses Hagan et al SABR function.
            if (strike <= cutOffStrike)
            {
                ValueDerivatives volatilityAdjoint = sabrFunction.volatilityAdjoint(forward, strike, timeToExpiry, sabrData);
                ValueDerivatives bsAdjoint         = BlackFormulaRepository.priceAdjoint(forward, strike, timeToExpiry, volatilityAdjoint.Value, putCall.Equals(PutCall.CALL));
                return(bsAdjoint.getDerivative(1) + bsAdjoint.getDerivative(3) * volatilityAdjoint.getDerivative(1));
            }
            // Uses extrapolation for call.
            double pDK = extrapolationDerivative(strike);

            if (putCall.Put)
            {     // Put by call/put parity
                pDK += 1.0;
            }
            return(pDK);
        }