Exemplo n.º 1
0
        public void testFlatHazardRate()
        {
            // Testing flat hazard rate...

            double         hazardRate      = 0.0100;
            Handle <Quote> hazardRateQuote = new Handle <Quote>(new SimpleQuote(hazardRate));
            DayCounter     dayCounter      = new Actual360();
            Calendar       calendar        = new TARGET();
            int            n = 20;

            double tolerance = 1.0e-10;
            Date   today     = Settings.evaluationDate();
            Date   startDate = today;
            Date   endDate   = startDate;

            FlatHazardRate flatHazardRate = new FlatHazardRate(today, hazardRateQuote, dayCounter);

            for (int i = 0; i < n; i++)
            {
                endDate = calendar.advance(endDate, 1, TimeUnit.Years);
                double t                   = dayCounter.yearFraction(startDate, endDate);
                double probability         = 1.0 - Math.Exp(-hazardRate * t);
                double computedProbability = flatHazardRate.defaultProbability(t);

                if (Math.Abs(probability - computedProbability) > tolerance)
                {
                    QAssert.Fail("Failed to reproduce probability for flat hazard rate\n"
                                 + "    calculated probability: " + computedProbability + "\n"
                                 + "    expected probability:   " + probability);
                }
            }
        }
Exemplo n.º 2
0
        public void testDefaultProbability()
        {
            // Testing default-probability structure...

            double         hazardRate      = 0.0100;
            Handle <Quote> hazardRateQuote = new Handle <Quote>(new SimpleQuote(hazardRate));
            DayCounter     dayCounter      = new Actual360();
            Calendar       calendar        = new TARGET();
            int            n = 20;

            double tolerance = 1.0e-10;
            Date   today     = Settings.evaluationDate();
            Date   startDate = today;
            Date   endDate   = startDate;

            FlatHazardRate flatHazardRate = new FlatHazardRate(startDate, hazardRateQuote, dayCounter);

            for (int i = 0; i < n; i++)
            {
                startDate = endDate;
                endDate   = calendar.advance(endDate, 1, TimeUnit.Years);

                double pStart = flatHazardRate.defaultProbability(startDate);
                double pEnd   = flatHazardRate.defaultProbability(endDate);

                double pBetweenComputed =
                    flatHazardRate.defaultProbability(startDate, endDate);

                double pBetween = pEnd - pStart;

                if (Math.Abs(pBetween - pBetweenComputed) > tolerance)
                {
                    QAssert.Fail("Failed to reproduce probability(d1, d2) "
                                 + "for default probability structure\n"
                                 + "    calculated probability: " + pBetweenComputed + "\n"
                                 + "    expected probability:   " + pBetween);
                }

                double t2 = dayCounter.yearFraction(today, endDate);
                double timeProbability = flatHazardRate.defaultProbability(t2);
                double dateProbability =
                    flatHazardRate.defaultProbability(endDate);

                if (Math.Abs(timeProbability - dateProbability) > tolerance)
                {
                    QAssert.Fail("single-time probability and single-date probability do not match\n"
                                 + "    time probability: " + timeProbability + "\n"
                                 + "    date probability: " + dateProbability);
                }

                double t1 = dayCounter.yearFraction(today, startDate);
                timeProbability = flatHazardRate.defaultProbability(t1, t2);
                dateProbability = flatHazardRate.defaultProbability(startDate, endDate);

                if (Math.Abs(timeProbability - dateProbability) > tolerance)
                {
                    QAssert.Fail("double-time probability and double-date probability do not match\n"
                                 + "    time probability: " + timeProbability + "\n"
                                 + "    date probability: " + dateProbability);
                }
            }
        }
Exemplo n.º 3
0
        public void testBSMOperatorConsistency()
        {
            //("Testing consistency of BSM operators...");

            Vector grid   = new Vector(10);
            double price  = 20.0;
            double factor = 1.1;

            for (int i = 0; i < grid.size(); i++)
            {
                grid[i] = price;
                price  *= factor;
            }

            double dx    = Math.Log(factor);
            double r     = 0.05;
            double q     = 0.01;
            double sigma = 0.5;

            BSMOperator refer = new BSMOperator(grid.size(), dx, r, q, sigma);

            DayCounter dc           = new Actual360();
            Date       today        = Date.Today;
            Date       exercise     = today + new Period(2, TimeUnit.Years);
            double     residualTime = dc.yearFraction(today, exercise);

            SimpleQuote                    spot         = new SimpleQuote(0.0);
            YieldTermStructure             qTS          = Utilities.flatRate(today, q, dc);
            YieldTermStructure             rTS          = Utilities.flatRate(today, r, dc);
            BlackVolTermStructure          volTS        = Utilities.flatVol(today, sigma, dc);
            GeneralizedBlackScholesProcess stochProcess = new GeneralizedBlackScholesProcess(
                new Handle <Quote>(spot),
                new Handle <YieldTermStructure>(qTS),
                new Handle <YieldTermStructure>(rTS),
                new Handle <BlackVolTermStructure>(volTS));
            BSMOperator          op1 = new BSMOperator(grid, stochProcess, residualTime);
            PdeOperator <PdeBSM> op2 = new PdeOperator <PdeBSM>(grid, stochProcess, residualTime);

            double tolerance = 1.0e-6;
            Vector lderror   = refer.lowerDiagonal() - op1.lowerDiagonal();
            Vector derror    = refer.diagonal() - op1.diagonal();
            Vector uderror   = refer.upperDiagonal() - op1.upperDiagonal();

            for (int i = 2; i < grid.size() - 2; i++)
            {
                if (Math.Abs(lderror[i]) > tolerance ||
                    Math.Abs(derror[i]) > tolerance ||
                    Math.Abs(uderror[i]) > tolerance)
                {
                    Assert.Fail("inconsistency between BSM operators:\n"
                                + i + " row:\n"
                                + "expected:   "
                                + refer.lowerDiagonal()[i] + ", "
                                + refer.diagonal()[i] + ", "
                                + refer.upperDiagonal()[i] + "\n"
                                + "calculated: "
                                + op1.lowerDiagonal()[i] + ", "
                                + op1.diagonal()[i] + ", "
                                + op1.upperDiagonal()[i]);
                }
            }
            lderror = refer.lowerDiagonal() - op2.lowerDiagonal();
            derror  = refer.diagonal() - op2.diagonal();
            uderror = refer.upperDiagonal() - op2.upperDiagonal();

            for (int i = 2; i < grid.size() - 2; i++)
            {
                if (Math.Abs(lderror[i]) > tolerance ||
                    Math.Abs(derror[i]) > tolerance ||
                    Math.Abs(uderror[i]) > tolerance)
                {
                    Assert.Fail("inconsistency between BSM operators:\n"
                                + i + " row:\n"
                                + "expected:   "
                                + refer.lowerDiagonal()[i] + ", "
                                + refer.diagonal()[i] + ", "
                                + refer.upperDiagonal()[i] + "\n"
                                + "calculated: "
                                + op2.lowerDiagonal()[i] + ", "
                                + op2.diagonal()[i] + ", "
                                + op2.upperDiagonal()[i]);
                }
            }
        }
Exemplo n.º 4
0
        private void testOptionGreeks(ForwardVanillaEngine.GetOriginalEngine getEngine)
        {
            SavedSettings backup = new SavedSettings();

            Dictionary <String, double> calculated = new Dictionary <string, double>(),
                                        expected   = new Dictionary <string, double>(),
                                        tolerance  = new Dictionary <string, double>();

            tolerance["delta"]  = 1.0e-5;
            tolerance["gamma"]  = 1.0e-5;
            tolerance["theta"]  = 1.0e-5;
            tolerance["rho"]    = 1.0e-5;
            tolerance["divRho"] = 1.0e-5;
            tolerance["vega"]   = 1.0e-5;

            Option.Type[] types       = { Option.Type.Call, Option.Type.Put };
            double[]      moneyness   = { 0.9, 1.0, 1.1 };
            double[]      underlyings = { 100.0 };
            double[]      qRates      = { 0.04, 0.05, 0.06 };
            double[]      rRates      = { 0.01, 0.05, 0.15 };
            int[]         lengths     = { 1, 2 };
            Frequency[]   frequencies = { Frequency.Semiannual, Frequency.Quarterly, };
            double[]      vols        = { 0.11, 0.50, 1.20 };

            DayCounter dc    = new Actual360();
            Date       today = Date.Today;

            Settings.setEvaluationDate(today);

            SimpleQuote spot  = new SimpleQuote(0.0);
            SimpleQuote qRate = new SimpleQuote(0.0);
            Handle <YieldTermStructure> qTS = new Handle <YieldTermStructure>(Utilities.flatRate(qRate, dc));
            SimpleQuote rRate = new SimpleQuote(0.0);
            Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>(Utilities.flatRate(rRate, dc));
            SimpleQuote vol = new SimpleQuote(0.0);
            Handle <BlackVolTermStructure> volTS = new Handle <BlackVolTermStructure>(Utilities.flatVol(vol, dc));

            BlackScholesMertonProcess process = new BlackScholesMertonProcess(new Handle <Quote>(spot), qTS, rTS, volTS);

            for (int i = 0; i < types.Length; i++)
            {
                for (int j = 0; j < moneyness.Length; j++)
                {
                    for (int k = 0; k < lengths.Length; k++)
                    {
                        for (int kk = 0; kk < frequencies.Length; kk++)
                        {
                            EuropeanExercise maturity = new EuropeanExercise(today + new Period(lengths[k], TimeUnit.Years));

                            PercentageStrikePayoff payoff = new PercentageStrikePayoff(types[i], moneyness[j]);

                            List <Date> reset = new List <Date>();
                            for (Date d = today + new Period(frequencies[kk]);
                                 d < maturity.lastDate();
                                 d += new Period(frequencies[kk]))
                            {
                                reset.Add(d);
                            }

                            IPricingEngine engine = getEngine(process);

                            CliquetOption option = new CliquetOption(payoff, maturity, reset);
                            option.setPricingEngine(engine);

                            for (int l = 0; l < underlyings.Length; l++)
                            {
                                for (int m = 0; m < qRates.Length; m++)
                                {
                                    for (int n = 0; n < rRates.Length; n++)
                                    {
                                        for (int p = 0; p < vols.Length; p++)
                                        {
                                            double u = underlyings[l];
                                            double q = qRates[m],
                                                   r = rRates[n];
                                            double v = vols[p];
                                            spot.setValue(u);
                                            qRate.setValue(q);
                                            rRate.setValue(r);
                                            vol.setValue(v);

                                            double value = option.NPV();
                                            calculated["delta"]  = option.delta();
                                            calculated["gamma"]  = option.gamma();
                                            calculated["theta"]  = option.theta();
                                            calculated["rho"]    = option.rho();
                                            calculated["divRho"] = option.dividendRho();
                                            calculated["vega"]   = option.vega();

                                            if (value > spot.value() * 1.0e-5)
                                            {
                                                // perturb spot and get delta and gamma
                                                double du = u * 1.0e-4;
                                                spot.setValue(u + du);
                                                double value_p = option.NPV(),
                                                       delta_p = option.delta();
                                                spot.setValue(u - du);
                                                double value_m = option.NPV(),
                                                       delta_m = option.delta();
                                                spot.setValue(u);
                                                expected["delta"] = (value_p - value_m) / (2 * du);
                                                expected["gamma"] = (delta_p - delta_m) / (2 * du);

                                                // perturb rates and get rho and dividend rho
                                                double dr = r * 1.0e-4;
                                                rRate.setValue(r + dr);
                                                value_p = option.NPV();
                                                rRate.setValue(r - dr);
                                                value_m = option.NPV();
                                                rRate.setValue(r);
                                                expected["rho"] = (value_p - value_m) / (2 * dr);

                                                double dq = q * 1.0e-4;
                                                qRate.setValue(q + dq);
                                                value_p = option.NPV();
                                                qRate.setValue(q - dq);
                                                value_m = option.NPV();
                                                qRate.setValue(q);
                                                expected["divRho"] = (value_p - value_m) / (2 * dq);

                                                // perturb volatility and get vega
                                                double dv = v * 1.0e-4;
                                                vol.setValue(v + dv);
                                                value_p = option.NPV();
                                                vol.setValue(v - dv);
                                                value_m = option.NPV();
                                                vol.setValue(v);
                                                expected["vega"] = (value_p - value_m) / (2 * dv);

                                                // perturb date and get theta
                                                double dT = dc.yearFraction(today - 1, today + 1);
                                                Settings.setEvaluationDate(today - 1);
                                                value_m = option.NPV();
                                                Settings.setEvaluationDate(today + 1);
                                                value_p = option.NPV();
                                                Settings.setEvaluationDate(today);
                                                expected["theta"] = (value_p - value_m) / dT;

                                                // compare
                                                foreach (var it in calculated)
                                                {
                                                    String greek = it.Key;
                                                    double expct = expected  [greek],
                                                           calcl = calculated[greek],
                                                           tol   = tolerance [greek];
                                                    double error = Utilities.relativeError(expct, calcl, u);
                                                    if (error > tol)
                                                    {
                                                        REPORT_FAILURE(greek, payoff, maturity,
                                                                       u, q, r, today, v,
                                                                       expct, calcl, error, tol);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        public void testDiscretizationError()
        {
            // Testing the discretization error of the Heston Hull-White process
            DayCounter dc    = new Actual360();
            Date       today = Date.Today;

            Settings.Instance.setEvaluationDate(today);

            // construct a strange yield curve to check drifts and discounting
            // of the joint stochastic process

            List <Date>   dates = new List <Date>();
            List <double> times = new List <double>();
            List <double> rates = new List <double>(), divRates = new List <double>();

            for (int i = 0; i <= 31; ++i)
            {
                dates.Add(today + new Period(i, TimeUnit.Years));
                // FLOATING_POINT_EXCEPTION
                rates.Add(0.04 + 0.0001 * Math.Exp(Math.Sin(i)));
                divRates.Add(0.04 + 0.0001 * Math.Exp(Math.Sin(i)));
                times.Add(dc.yearFraction(today, dates.Last()));
            }

            Date   maturity = today + new Period(10, TimeUnit.Years);
            double v        = 0.25;

            Handle <Quote> s0  = new Handle <Quote>(new SimpleQuote(100));
            SimpleQuote    vol = new SimpleQuote(v);
            Handle <BlackVolTermStructure> volTS = new Handle <BlackVolTermStructure>(Utilities.flatVol(today, vol, dc));
            Handle <YieldTermStructure>    rTS   = new Handle <YieldTermStructure>(new InterpolatedZeroCurve <Linear>(dates, rates, dc));
            Handle <YieldTermStructure>    qTS   = new Handle <YieldTermStructure>(new InterpolatedZeroCurve <Linear>(dates, divRates, dc));

            BlackScholesMertonProcess bsmProcess = new BlackScholesMertonProcess(s0, qTS, rTS, volTS);

            HestonProcess hestonProcess = new HestonProcess(rTS, qTS, s0, v * v, 1, v * v, 1e-6, -0.4);

            HullWhiteForwardProcess hwProcess = new HullWhiteForwardProcess(rTS, 0.01, 0.01);

            hwProcess.setForwardMeasureTime(20.1472222222222222);

            double tol = 0.05;

            double[] corr   = { -0.85, 0.5 };
            double[] strike = { 50, 100, 125 };

            for (int i = 0; i < corr.Length; ++i)
            {
                for (int j = 0; j < strike.Length; ++j)
                {
                    StrikedTypePayoff payoff   = new PlainVanillaPayoff(Option.Type.Put, strike[j]);
                    Exercise          exercise = new EuropeanExercise(maturity);

                    VanillaOption optionBsmHW = new VanillaOption(payoff, exercise);
                    HullWhite     hwModel     = new HullWhite(rTS, hwProcess.a(), hwProcess.sigma());
                    optionBsmHW.setPricingEngine(new AnalyticBSMHullWhiteEngine(corr[i], bsmProcess, hwModel));

                    double expected = optionBsmHW.NPV();

                    VanillaOption optionHestonHW = new VanillaOption(payoff, exercise);
                    HybridHestonHullWhiteProcess jointProcess = new HybridHestonHullWhiteProcess(hestonProcess,
                                                                                                 hwProcess, corr[i]);
                    optionHestonHW.setPricingEngine(
                        new MakeMCHestonHullWhiteEngine <PseudoRandom, Statistics>(jointProcess)
                        .withSteps(1)
                        .withAntitheticVariate()
                        .withAbsoluteTolerance(tol)
                        .withSeed(42).getAsPricingEngine());

                    double calculated = optionHestonHW.NPV();
                    double error      = optionHestonHW.errorEstimate();

                    if ((Math.Abs(calculated - expected) > 3 * error &&
                         Math.Abs(calculated - expected) > 1e-5))
                    {
                        QAssert.Fail("Failed to reproduce discretization error"
                                     + "\n   corr:       " + corr[i]
                                     + "\n   strike:     " + strike[j]
                                     + "\n   calculated: " + calculated
                                     + "\n   error:      " + error
                                     + "\n   expected:   " + expected);
                    }
                }
            }
        }
Exemplo n.º 6
0
        public void testEuropeanGreeks()
        {
            // Testing dividend European option greeks...

            SavedSettings backup = new SavedSettings();

            Dictionary <string, double> calculated = new Dictionary <string, double>(),
                                        expected   = new Dictionary <string, double>(),
                                        tolerance  = new Dictionary <string, double>();

            tolerance["delta"] = 1.0e-5;
            tolerance["gamma"] = 1.0e-5;
            tolerance["theta"] = 1.0e-5;
            tolerance["rho"]   = 1.0e-5;
            tolerance["vega"]  = 1.0e-5;

            Option.Type[] types       = { Option.Type.Call, Option.Type.Put };
            double[]      strikes     = { 50.0, 99.5, 100.0, 100.5, 150.0 };
            double[]      underlyings = { 100.0 };
            double[]      qRates      = { 0.00, 0.10, 0.30 };
            double[]      rRates      = { 0.01, 0.05, 0.15 };
            int[]         lengths     = { 1, 2 };
            double[]      vols        = { 0.05, 0.20, 0.40 };

            DayCounter dc    = new Actual360();
            Date       today = Date.Today;

            Settings.setEvaluationDate(today);

            SimpleQuote spot  = new SimpleQuote(0.0);
            SimpleQuote qRate = new SimpleQuote(0.0);
            Handle <YieldTermStructure> qTS = new Handle <YieldTermStructure>(Utilities.flatRate(qRate, dc));
            SimpleQuote rRate = new SimpleQuote(0.0);
            Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>(Utilities.flatRate(rRate, dc));
            SimpleQuote vol = new SimpleQuote(0.0);
            Handle <BlackVolTermStructure> volTS = new Handle <BlackVolTermStructure>(Utilities.flatVol(vol, dc));

            for (int i = 0; i < types.Length; i++)
            {
                for (int j = 0; j < strikes.Length; j++)
                {
                    for (int k = 0; k < lengths.Length; k++)
                    {
                        Date     exDate   = today + new Period(lengths[k], TimeUnit.Years);
                        Exercise exercise = new EuropeanExercise(exDate);

                        List <Date>   dividendDates = new List <Date>();
                        List <double> dividends     = new List <double>();
                        for (Date d = today + new Period(3, TimeUnit.Months);
                             d < exercise.lastDate();
                             d += new Period(6, TimeUnit.Months))
                        {
                            dividendDates.Add(d);
                            dividends.Add(5.0);
                        }

                        StrikedTypePayoff payoff = new PlainVanillaPayoff(types[i], strikes[j]);

                        BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(new Handle <Quote>(spot),
                                                                                               qTS, rTS, volTS);

                        IPricingEngine engine = new AnalyticDividendEuropeanEngine(stochProcess);

                        DividendVanillaOption option = new DividendVanillaOption(payoff, exercise, dividendDates,
                                                                                 dividends);
                        option.setPricingEngine(engine);

                        for (int l = 0; l < underlyings.Length; l++)
                        {
                            for (int m = 0; m < qRates.Length; m++)
                            {
                                for (int n = 0; n < rRates.Length; n++)
                                {
                                    for (int p = 0; p < vols.Length; p++)
                                    {
                                        double u = underlyings[l];
                                        double q = qRates[m],
                                               r = rRates[n];
                                        double v = vols[p];
                                        spot.setValue(u);
                                        qRate.setValue(q);
                                        rRate.setValue(r);
                                        vol.setValue(v);

                                        double value = option.NPV();
                                        calculated["delta"] = option.delta();
                                        calculated["gamma"] = option.gamma();
                                        calculated["theta"] = option.theta();
                                        calculated["rho"]   = option.rho();
                                        calculated["vega"]  = option.vega();

                                        if (value > spot.value() * 1.0e-5)
                                        {
                                            // perturb spot and get delta and gamma
                                            double du = u * 1.0e-4;
                                            spot.setValue(u + du);
                                            double value_p = option.NPV(),
                                                   delta_p = option.delta();
                                            spot.setValue(u - du);
                                            double value_m = option.NPV(),
                                                   delta_m = option.delta();
                                            spot.setValue(u);
                                            expected["delta"] = (value_p - value_m) / (2 * du);
                                            expected["gamma"] = (delta_p - delta_m) / (2 * du);

                                            // perturb risk-free rate and get rho
                                            double dr = r * 1.0e-4;
                                            rRate.setValue(r + dr);
                                            value_p = option.NPV();
                                            rRate.setValue(r - dr);
                                            value_m = option.NPV();
                                            rRate.setValue(r);
                                            expected["rho"] = (value_p - value_m) / (2 * dr);

                                            // perturb volatility and get vega
                                            double dv = v * 1.0e-4;
                                            vol.setValue(v + dv);
                                            value_p = option.NPV();
                                            vol.setValue(v - dv);
                                            value_m = option.NPV();
                                            vol.setValue(v);
                                            expected["vega"] = (value_p - value_m) / (2 * dv);

                                            // perturb date and get theta
                                            double dT = dc.yearFraction(today - 1, today + 1);
                                            Settings.setEvaluationDate(today - 1);
                                            value_m = option.NPV();
                                            Settings.setEvaluationDate(today + 1);
                                            value_p = option.NPV();
                                            Settings.setEvaluationDate(today);
                                            expected["theta"] = (value_p - value_m) / dT;

                                            // compare
                                            foreach (KeyValuePair <string, double> it in calculated)
                                            {
                                                string greek = it.Key;
                                                double expct = expected  [greek],
                                                       calcl = calculated[greek],
                                                       tol   = tolerance [greek];
                                                double error = Utilities.relativeError(expct, calcl, u);
                                                if (error > tol)
                                                {
                                                    REPORT_FAILURE(greek, payoff, exercise,
                                                                   u, q, r, today, v,
                                                                   expct, calcl, error, tol);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        public void testAnalyticHestonHullWhitePricing()
        {
            // Testing analytic Heston Hull-White option pricing
            DayCounter dc    = new Actual360();
            Date       today = Date.Today;

            Settings.Instance.setEvaluationDate(today);

            // construct a strange yield curve to check drifts and discounting
            // of the joint stochastic process

            List <Date>   dates = new List <Date>();
            List <double> times = new List <double>();
            List <double> rates = new List <double>(), divRates = new List <double>();

            for (int i = 0; i <= 40; ++i)
            {
                dates.Add(today + new Period(i, TimeUnit.Years));
                // FLOATING_POINT_EXCEPTION
                rates.Add(0.03 + 0.0001 * Math.Exp(Math.Sin(i / 4.0)));
                divRates.Add(0.02 + 0.0002 * Math.Exp(Math.Sin(i / 3.0)));
                times.Add(dc.yearFraction(today, dates.Last()));
            }

            Date           maturity         = today + new Period(5, TimeUnit.Years);
            Handle <Quote> s0               = new Handle <Quote>(new SimpleQuote(100));
            Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>(new InterpolatedZeroCurve <Linear>(dates, rates, dc));
            Handle <YieldTermStructure> qTS = new Handle <YieldTermStructure>(new InterpolatedZeroCurve <Linear>(dates, divRates, dc));

            HestonProcess hestonProcess = new HestonProcess(rTS, qTS, s0, 0.08, 1.5, 0.0625, 0.5, -0.8);
            HestonModel   hestonModel   = new HestonModel(hestonProcess);

            HullWhiteForwardProcess hwFwdProcess = new HullWhiteForwardProcess(rTS, 0.01, 0.01);

            hwFwdProcess.setForwardMeasureTime(dc.yearFraction(today, maturity));
            HullWhite hullWhiteModel = new HullWhite(rTS, hwFwdProcess.a(), hwFwdProcess.sigma());

            double tol = 0.002;

            double[]      strike = { 80, 120 };
            Option.Type[] types  = { Option.Type.Put, Option.Type.Call };

            for (int i = 0; i < types.Length; ++i)
            {
                for (int j = 0; j < strike.Length; ++j)
                {
                    HybridHestonHullWhiteProcess jointProcess = new HybridHestonHullWhiteProcess(hestonProcess,
                                                                                                 hwFwdProcess, 0.0, HybridHestonHullWhiteProcess.Discretization.Euler);

                    StrikedTypePayoff payoff   = new PlainVanillaPayoff(types[i], strike[j]);
                    Exercise          exercise = new EuropeanExercise(maturity);

                    VanillaOption optionHestonHW = new VanillaOption(payoff, exercise);
                    optionHestonHW.setPricingEngine(new MakeMCHestonHullWhiteEngine <PseudoRandom, Statistics>(jointProcess)
                                                    .withSteps(1)
                                                    .withAntitheticVariate()
                                                    .withControlVariate()
                                                    .withAbsoluteTolerance(tol)
                                                    .withSeed(42).getAsPricingEngine());

                    VanillaOption optionPureHeston = new VanillaOption(payoff, exercise);
                    optionPureHeston.setPricingEngine(new AnalyticHestonHullWhiteEngine(hestonModel, hullWhiteModel, 128));

                    double calculated = optionHestonHW.NPV();
                    double error      = optionHestonHW.errorEstimate();
                    double expected   = optionPureHeston.NPV();

                    if (Math.Abs(calculated - expected) > 3 * error &&
                        Math.Abs(calculated - expected) > tol)
                    {
                        QAssert.Fail("Failed to reproduce hw heston vanilla prices"
                                     + "\n   strike:     " + strike[j]
                                     + "\n   calculated: " + calculated
                                     + "\n   error:      " + error
                                     + "\n   expected:   " + expected);
                    }
                }
            }
        }
        public void testZeroBondPricing()
        {
            // Testing Monte-Carlo zero bond pricing

            DayCounter dc    = new Actual360();
            Date       today = Date.Today;

            Settings.Instance.setEvaluationDate(today);

            // construct a strange yield curve to check drifts and discounting
            // of the joint stochastic process

            List <Date>   dates = new List <Date>();
            List <double> times = new List <double>();
            List <double> rates = new List <double>();

            dates.Add(today);
            rates.Add(0.02);
            times.Add(0.0);
            for (int i = 120; i < 240; ++i)
            {
                dates.Add(today + new Period(i, TimeUnit.Months));
                rates.Add(0.02 + 0.0002 * Math.Exp(Math.Sin(i / 8.0)));
                times.Add(dc.yearFraction(today, dates.Last()));
            }

            Date maturity = dates.Last() + new Period(10, TimeUnit.Years);

            dates.Add(maturity);
            rates.Add(0.04);
            //times.Add(dc.yearFraction(today, dates.Last()));

            Handle <Quote> s0 = new Handle <Quote>(new SimpleQuote(100));

            Handle <YieldTermStructure> ts = new Handle <YieldTermStructure>(new InterpolatedZeroCurve <Linear>(dates, rates, dc));
            Handle <YieldTermStructure> ds = new Handle <YieldTermStructure>(Utilities.flatRate(today, 0.0, dc));

            HestonProcess           hestonProcess = new HestonProcess(ts, ds, s0, 0.02, 1.0, 0.2, 0.5, -0.8);
            HullWhiteForwardProcess hwProcess     = new HullWhiteForwardProcess(ts, 0.05, 0.05);

            hwProcess.setForwardMeasureTime(dc.yearFraction(today, maturity));
            HullWhite hwModel = new HullWhite(ts, 0.05, 0.05);

            HybridHestonHullWhiteProcess jointProcess = new HybridHestonHullWhiteProcess(hestonProcess, hwProcess, -0.4);

            TimeGrid grid = new TimeGrid(times);

            int factors = jointProcess.factors();
            int steps   = grid.size() - 1;
            SobolBrownianBridgeRsg rsg = new SobolBrownianBridgeRsg(factors, steps);
            MultiPathGenerator <SobolBrownianBridgeRsg> generator = new MultiPathGenerator <SobolBrownianBridgeRsg>(
                jointProcess, grid, rsg, false);

            int m = 90;
            List <GeneralStatistics> zeroStat   = new InitializedList <GeneralStatistics>(m);
            List <GeneralStatistics> optionStat = new InitializedList <GeneralStatistics>(m);

            int    nrTrails    = 8191;
            int    optionTenor = 24;
            double strike      = 0.5;

            for (int i = 0; i < nrTrails; ++i)
            {
                Sample <IPath> path  = generator.next();
                MultiPath      value = path.value as MultiPath;
                Utils.QL_REQUIRE(value != null, () => "Invalid Path");

                for (int j = 1; j < m; ++j)
                {
                    double t = grid[j];               // zero end and option maturity
                    double T = grid[j + optionTenor]; // maturity of zero bond
                    // of option

                    Vector states       = new Vector(3);
                    Vector optionStates = new Vector(3);
                    for (int k = 0; k < jointProcess.size(); ++k)
                    {
                        states[k]       = value[k][j];
                        optionStates[k] = value[k][j + optionTenor];
                    }

                    double zeroBond
                        = 1.0 / jointProcess.numeraire(t, states);
                    double zeroOption = zeroBond * Math.Max(0.0, hwModel.discountBond(t, T, states[2]) - strike);

                    zeroStat[j].add(zeroBond);
                    optionStat[j].add(zeroOption);
                }
            }

            for (int j = 1; j < m; ++j)
            {
                double t          = grid[j];
                double calculated = zeroStat[j].mean();
                double expected   = ts.link.discount(t);

                if (Math.Abs(calculated - expected) > 0.03)
                {
                    QAssert.Fail("Failed to reproduce expected zero bond prices"
                                 + "\n   t:          " + t
                                 + "\n   calculated: " + calculated
                                 + "\n   expected:   " + expected);
                }

                double T = grid[j + optionTenor];

                calculated = optionStat[j].mean();
                expected   = hwModel.discountBondOption(Option.Type.Call, strike, t, T);

                if (Math.Abs(calculated - expected) > 0.0035)
                {
                    QAssert.Fail("Failed to reproduce expected zero bond option prices"
                                 + "\n   t:          " + t
                                 + "\n   T:          " + T
                                 + "\n   calculated: " + calculated
                                 + "\n   expected:   " + expected);
                }
            }
        }
Exemplo n.º 9
0
        public void testBSMOperatorConsistency()
        {
            //("Testing consistency of BSM operators...");

             Vector grid = new Vector(10);
             double price = 20.0;
             double factor = 1.1;
             for (int i = 0; i < grid.size(); i++)
             {
            grid[i] = price;
            price *= factor;
             }

             double dx = Math.Log(factor);
             double r = 0.05;
             double q = 0.01;
             double sigma = 0.5;

             BSMOperator refer = new BSMOperator(grid.size(), dx, r, q, sigma);

             DayCounter dc = new Actual360();
             Date today = Date.Today;
             Date exercise = today + new Period(2, TimeUnit.Years);
             double residualTime = dc.yearFraction(today, exercise);

             SimpleQuote spot = new SimpleQuote(0.0);
             YieldTermStructure qTS = Utilities.flatRate(today, q, dc);
             YieldTermStructure rTS = Utilities.flatRate(today, r, dc);
             BlackVolTermStructure volTS = Utilities.flatVol(today, sigma, dc);
             GeneralizedBlackScholesProcess stochProcess = new GeneralizedBlackScholesProcess(
                                                        new Handle<Quote>(spot),
                                                        new Handle<YieldTermStructure>(qTS),
                                                        new Handle<YieldTermStructure>(rTS),
                                                        new Handle<BlackVolTermStructure>(volTS));
             BSMOperator op1 = new BSMOperator(grid, stochProcess, residualTime);
             PdeOperator<PdeBSM> op2 = new PdeOperator<PdeBSM>(grid, stochProcess, residualTime);

             double tolerance = 1.0e-6;
             Vector lderror = refer.lowerDiagonal() - op1.lowerDiagonal();
             Vector derror = refer.diagonal() - op1.diagonal();
             Vector uderror = refer.upperDiagonal() - op1.upperDiagonal();

             for (int i = 2; i < grid.size() - 2; i++)
             {
            if (Math.Abs(lderror[i]) > tolerance ||
                Math.Abs(derror[i]) > tolerance ||
                Math.Abs(uderror[i]) > tolerance)
            {
               Assert.Fail("inconsistency between BSM operators:\n"
                          + i + " row:\n"
                          + "expected:   "
                          + refer.lowerDiagonal()[i] + ", "
                          + refer.diagonal()[i] + ", "
                          + refer.upperDiagonal()[i] + "\n"
                          + "calculated: "
                          + op1.lowerDiagonal()[i] + ", "
                          + op1.diagonal()[i] + ", "
                          + op1.upperDiagonal()[i]);
            }
             }
             lderror = refer.lowerDiagonal() - op2.lowerDiagonal();
             derror = refer.diagonal() - op2.diagonal();
             uderror = refer.upperDiagonal() - op2.upperDiagonal();

             for (int i = 2; i < grid.size() - 2; i++)
             {
            if (Math.Abs(lderror[i]) > tolerance ||
                Math.Abs(derror[i]) > tolerance ||
                Math.Abs(uderror[i]) > tolerance)
            {
               Assert.Fail("inconsistency between BSM operators:\n"
                          + i + " row:\n"
                          + "expected:   "
                          + refer.lowerDiagonal()[i] + ", "
                          + refer.diagonal()[i] + ", "
                          + refer.upperDiagonal()[i] + "\n"
                          + "calculated: "
                          + op2.lowerDiagonal()[i] + ", "
                          + op2.diagonal()[i] + ", "
                          + op2.upperDiagonal()[i]);
            }
             }
        }
Exemplo n.º 10
0
        private void testForwardGreeks(Type engine_type)
        {
            Dictionary <String, double> calculated = new Dictionary <string, double>(),
                                        expected   = new Dictionary <string, double>(),
                                        tolerance  = new Dictionary <string, double>();

            tolerance["delta"]  = 1.0e-5;
            tolerance["gamma"]  = 1.0e-5;
            tolerance["theta"]  = 1.0e-5;
            tolerance["rho"]    = 1.0e-5;
            tolerance["divRho"] = 1.0e-5;
            tolerance["vega"]   = 1.0e-5;

            Option.Type[] types       = { Option.Type.Call, Option.Type.Put };
            double[]      moneyness   = { 0.9, 1.0, 1.1 };
            double[]      underlyings = { 100.0 };
            double[]      qRates      = { 0.04, 0.05, 0.06 };
            double[]      rRates      = { 0.01, 0.05, 0.15 };
            int[]         lengths     = { 1, 2 };
            int[]         startMonths = { 6, 9 };
            double[]      vols        = { 0.11, 0.50, 1.20 };

            DayCounter dc    = new Actual360();
            Date       today = Date.Today;

            Settings.setEvaluationDate(today);

            SimpleQuote spot  = new SimpleQuote(0.0);
            SimpleQuote qRate = new SimpleQuote(0.0);
            Handle <YieldTermStructure> qTS = new Handle <YieldTermStructure>(Utilities.flatRate(qRate, dc));
            SimpleQuote rRate = new SimpleQuote(0.0);
            Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>(Utilities.flatRate(rRate, dc));
            SimpleQuote vol = new SimpleQuote(0.0);
            Handle <BlackVolTermStructure> volTS = new Handle <BlackVolTermStructure>(Utilities.flatVol(vol, dc));

            BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(new Handle <Quote>(spot), qTS, rTS, volTS);

            IPricingEngine engine = engine_type == typeof(ForwardVanillaEngine) ? new ForwardVanillaEngine(stochProcess, process => new AnalyticEuropeanEngine(process)) :
                                    new ForwardPerformanceVanillaEngine(stochProcess, process => new AnalyticEuropeanEngine(process));

            for (int i = 0; i < types.Length; i++)
            {
                for (int j = 0; j < moneyness.Length; j++)
                {
                    for (int k = 0; k < lengths.Length; k++)
                    {
                        for (int h = 0; h < startMonths.Length; h++)
                        {
                            Date     exDate   = today + new Period(lengths[k], TimeUnit.Years);
                            Exercise exercise = new EuropeanExercise(exDate);

                            Date reset = today + new Period(startMonths[h], TimeUnit.Months);

                            StrikedTypePayoff payoff = new PlainVanillaPayoff(types[i], 0.0);

                            ForwardVanillaOption option = new ForwardVanillaOption(moneyness[j], reset, payoff, exercise);
                            option.setPricingEngine(engine);

                            for (int l = 0; l < underlyings.Length; l++)
                            {
                                for (int m = 0; m < qRates.Length; m++)
                                {
                                    for (int n = 0; n < rRates.Length; n++)
                                    {
                                        for (int p = 0; p < vols.Length; p++)
                                        {
                                            double u = underlyings[l];
                                            double q = qRates[m],
                                                   r = rRates[n];
                                            double v = vols[p];
                                            spot.setValue(u);
                                            qRate.setValue(q);
                                            rRate.setValue(r);
                                            vol.setValue(v);

                                            double value = option.NPV();
                                            calculated["delta"]  = option.delta();
                                            calculated["gamma"]  = option.gamma();
                                            calculated["theta"]  = option.theta();
                                            calculated["rho"]    = option.rho();
                                            calculated["divRho"] = option.dividendRho();
                                            calculated["vega"]   = option.vega();

                                            if (value > spot.value() * 1.0e-5)
                                            {
                                                // perturb spot and get delta and gamma
                                                double du = u * 1.0e-4;
                                                spot.setValue(u + du);
                                                double value_p = option.NPV(),
                                                       delta_p = option.delta();
                                                spot.setValue(u - du);
                                                double value_m = option.NPV(),
                                                       delta_m = option.delta();
                                                spot.setValue(u);
                                                expected["delta"] = (value_p - value_m) / (2 * du);
                                                expected["gamma"] = (delta_p - delta_m) / (2 * du);

                                                // perturb rates and get rho and dividend rho
                                                double dr = r * 1.0e-4;
                                                rRate.setValue(r + dr);
                                                value_p = option.NPV();
                                                rRate.setValue(r - dr);
                                                value_m = option.NPV();
                                                rRate.setValue(r);
                                                expected["rho"] = (value_p - value_m) / (2 * dr);

                                                double dq = q * 1.0e-4;
                                                qRate.setValue(q + dq);
                                                value_p = option.NPV();
                                                qRate.setValue(q - dq);
                                                value_m = option.NPV();
                                                qRate.setValue(q);
                                                expected["divRho"] = (value_p - value_m) / (2 * dq);

                                                // perturb volatility and get vega
                                                double dv = v * 1.0e-4;
                                                vol.setValue(v + dv);
                                                value_p = option.NPV();
                                                vol.setValue(v - dv);
                                                value_m = option.NPV();
                                                vol.setValue(v);
                                                expected["vega"] = (value_p - value_m) / (2 * dv);

                                                // perturb date and get theta
                                                double dT = dc.yearFraction(today - 1, today + 1);
                                                Settings.setEvaluationDate(today - 1);
                                                value_m = option.NPV();
                                                Settings.setEvaluationDate(today + 1);
                                                value_p = option.NPV();
                                                Settings.setEvaluationDate(today);
                                                expected["theta"] = (value_p - value_m) / dT;

                                                // compare
                                                //std::map<std::string,double>::iterator it;
                                                foreach (KeyValuePair <string, double> it in calculated)
                                                {
                                                    String greek = it.Key;
                                                    double expct = expected  [greek],
                                                           calcl = calculated[greek],
                                                           tol   = tolerance [greek];
                                                    double error = Utilities.relativeError(expct, calcl, u);
                                                    if (error > tol)
                                                    {
                                                        REPORT_FAILURE(greek, payoff, exercise,
                                                                       u, q, r, today, v,
                                                                       moneyness[j], reset,
                                                                       expct, calcl, error, tol);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
Exemplo n.º 11
0
        public void testAnalyticDiscreteGeometricAveragePriceGreeks()
        {
            //BOOST_MESSAGE("Testing discrete-averaging geometric Asian greeks...");

             //SavedSettings backup;

             Dictionary<string,double> calculated, expected, tolerance;
             calculated = new Dictionary<string, double>(6);
             expected = new Dictionary<string, double>(6);
             tolerance = new Dictionary<string, double>(6);
             tolerance["delta"]  = 1.0e-5;
             tolerance["gamma"]  = 1.0e-5;
             tolerance["theta"]  = 1.0e-5;
             tolerance["rho"]    = 1.0e-5;
             tolerance["divRho"] = 1.0e-5;
             tolerance["vega"]   = 1.0e-5;

             Option.Type[] types = { Option.Type.Call, Option.Type.Put };
             double[] underlyings = { 100.0 };
             double[] strikes = { 90.0, 100.0, 110.0 };
             double[] qRates = { 0.04, 0.05, 0.06 };
             double[] rRates = { 0.01, 0.05, 0.15 };
             int[] lengths = { 1, 2 };
             double[] vols = { 0.11, 0.50, 1.20 };

             DayCounter dc = new Actual360();
             Date today = Date.Today;
             Settings.setEvaluationDate(today);

             SimpleQuote spot = new SimpleQuote(0.0);
             SimpleQuote qRate = new SimpleQuote(0.0);
             Handle<YieldTermStructure> qTS = new Handle<YieldTermStructure>
                                                    (Utilities.flatRate(qRate, dc));
             SimpleQuote rRate = new SimpleQuote(0.0);
             Handle<YieldTermStructure> rTS = new Handle<YieldTermStructure>
                                                    (Utilities.flatRate(rRate, dc));
             SimpleQuote vol = new SimpleQuote(0.0);
             Handle<BlackVolTermStructure> volTS = new Handle<BlackVolTermStructure>
                                                    (Utilities.flatVol(vol, dc));

             BlackScholesMertonProcess process =
                  new BlackScholesMertonProcess(new Handle<Quote>(spot), qTS, rTS, volTS);

             for (int i=0; i<types.Length ; i++) {
               for (int j=0; j<strikes.Length ; j++) {
                 for (int k=0; k<lengths.Length ; k++) {

                     EuropeanExercise maturity =
                                       new EuropeanExercise(
                                           today + new Period(lengths[k],TimeUnit.Years));

                     PlainVanillaPayoff payoff =
                                         new PlainVanillaPayoff(types[i], strikes[j]);

                     double runningAverage = 120;
                     int pastFixings = 1;

                     List<Date> fixingDates = new List<Date>();
                     for (Date d = today + new Period(3, TimeUnit.Months);
                               d <= maturity.lastDate();
                               d += new Period(3, TimeUnit.Months))
                         fixingDates.Add(d);

                     IPricingEngine engine =
                        new AnalyticDiscreteGeometricAveragePriceAsianEngine(process);

                     DiscreteAveragingAsianOption option =
                         new DiscreteAveragingAsianOption(Average.Type.Geometric,
                                                         runningAverage, pastFixings,
                                                         fixingDates, payoff, maturity);
                     option.setPricingEngine(engine);

                     for (int l=0; l<underlyings.Length ; l++) {
                       for (int m=0; m<qRates.Length ; m++) {
                         for (int n=0; n<rRates.Length ; n++) {
                           for (int p=0; p<vols.Length ; p++) {

                               double u = underlyings[l];
                               double q = qRates[m],
                                    r = rRates[n];
                               double v = vols[p];
                               spot.setValue(u);
                               qRate.setValue(q);
                               rRate.setValue(r);
                               vol.setValue(v);

                               double value = option.NPV();
                               calculated["delta"]  = option.delta();
                               calculated["gamma"]  = option.gamma();
                               calculated["theta"]  = option.theta();
                               calculated["rho"]    = option.rho();
                               calculated["divRho"] = option.dividendRho();
                               calculated["vega"]   = option.vega();

                               if (value > spot.value()*1.0e-5) {
                                   // perturb spot and get delta and gamma
                                   double du = u*1.0e-4;
                                   spot.setValue(u+du);
                                   double value_p = option.NPV(),
                                        delta_p = option.delta();
                                   spot.setValue(u-du);
                                   double value_m = option.NPV(),
                                        delta_m = option.delta();
                                   spot.setValue(u);
                                   expected["delta"] = (value_p - value_m)/(2*du);
                                   expected["gamma"] = (delta_p - delta_m)/(2*du);

                                   // perturb rates and get rho and dividend rho
                                   double dr = r*1.0e-4;
                                   rRate.setValue(r+dr);
                                   value_p = option.NPV();
                                   rRate.setValue(r-dr);
                                   value_m = option.NPV();
                                   rRate.setValue(r);
                                   expected["rho"] = (value_p - value_m)/(2*dr);

                                   double dq = q*1.0e-4;
                                   qRate.setValue(q+dq);
                                   value_p = option.NPV();
                                   qRate.setValue(q-dq);
                                   value_m = option.NPV();
                                   qRate.setValue(q);
                                   expected["divRho"] = (value_p - value_m)/(2*dq);

                                   // perturb volatility and get vega
                                   double dv = v*1.0e-4;
                                   vol.setValue(v+dv);
                                   value_p = option.NPV();
                                   vol.setValue(v-dv);
                                   value_m = option.NPV();
                                   vol.setValue(v);
                                   expected["vega"] = (value_p - value_m)/(2*dv);

                                   // perturb date and get theta
                                   double dT = dc.yearFraction(today-1, today+1);
                                   Settings.setEvaluationDate(today-1);
                                   value_m = option.NPV();
                                   Settings.setEvaluationDate(today+1);
                                   value_p = option.NPV();
                                   Settings.setEvaluationDate(today);
                                   expected["theta"] = (value_p - value_m)/dT;

                                   // compare
                                   foreach (KeyValuePair<string, double> kvp in calculated){
                                       string greek = kvp.Key;
                                       double expct = expected[greek],
                                            calcl = calculated[greek],
                                            tol   = tolerance [greek];
                                       double error =Utilities.relativeError(expct,calcl,u);
                                       if (error>tol) {
                                           REPORT_FAILURE(greek, Average.Type.Geometric,
                                                          runningAverage, pastFixings,
                                                          new List<Date>(),
                                                          payoff, maturity,
                                                          u, q, r, today, v,
                                                          expct, calcl, tol);
                                       }
                                   }
                               }
                           }
                         }
                       }
                     }
                 }
               }
             }
        }