Ejemplo n.º 1
0
def heston_calibration(df_option, ival=None):
    """
    calibrate heston model
    """

    # extract rates and div yields from the data set
    df_tmp = DataFrame.filter(df_option, items=["dtExpiry", "iRate", "iDiv"])
    grouped = df_tmp.groupby("dtExpiry")
    df_rates = grouped.agg(lambda x: x[0])

    dtTrade = df_option["dtTrade"][0]
    # back out the spot from any forward
    iRate = df_option["iRate"][0]
    iDiv = df_option["iDiv"][0]
    TTM = df_option["TTM"][0]
    Fwd = df_option["Fwd"][0]
    spot = SimpleQuote(Fwd * np.exp(-(iRate - iDiv) * TTM))
    print("Spot: %f risk-free rate: %f div. yield: %f" % (spot.value, iRate, iDiv))

    # build array of option helpers
    hh = heston_helpers(spot, df_option, dtTrade, df_rates)
    options = hh["options"]
    spot = hh["spot"]

    risk_free_ts = dfToZeroCurve(df_rates["iRate"], dtTrade)
    dividend_ts = dfToZeroCurve(df_rates["iDiv"], dtTrade)

    # initial values for parameters
    if ival is None:
        ival = {"v0": 0.1, "kappa": 1.0, "theta": 0.1, "sigma": 0.5, "rho": -0.5}

    process = HestonProcess(
        risk_free_ts, dividend_ts, spot, ival["v0"], ival["kappa"], ival["theta"], ival["sigma"], ival["rho"]
    )

    model = HestonModel(process)
    engine = AnalyticHestonEngine(model, 64)

    for option in options:
        option.set_pricing_engine(engine)

    om = LevenbergMarquardt(1e-8, 1e-8, 1e-8)
    model.calibrate(options, om, EndCriteria(400, 40, 1.0e-8, 1.0e-8, 1.0e-8))

    print("model calibration results:")
    print("v0: %f kappa: %f theta: %f sigma: %f rho: %f" % (model.v0, model.kappa, model.theta, model.sigma, model.rho))

    calib_error = (1.0 / len(options)) * sum([pow(o.calibration_error() * 100.0, 2) for o in options])

    print("SSE: %f" % calib_error)

    # merge the fitted volatility and the input data set
    return merge_df(df_option, options, "Heston")
Ejemplo n.º 2
0
    def test_smith(self):
        # test against result published in
        # Journal of Computational Finance Vol. 11/1 Fall 2007
        # An almost exact simulation method for the heston model

        settlement_date = today()
        self.settings.evaluation_date = settlement_date

        daycounter = ActualActual()
        timeToMaturity = 4

        exercise_date = settlement_date + timeToMaturity * 365

        c_payoff = PlainVanillaPayoff(Call, 100)

        exercise = EuropeanExercise(exercise_date)

        risk_free_ts = flat_rate(0., daycounter)
        dividend_ts = flat_rate(0., daycounter)

        s0 = SimpleQuote(100.0)

        v0 = 0.0194
        kappa = 1.0407
        theta = 0.0586
        sigma = 0.5196
        rho = -.6747

        nb_steps_a = 100
        nb_paths = 20000
        seed = 12347

        process = HestonProcess(risk_free_ts, dividend_ts, s0, v0, kappa,
                                theta, sigma, rho, QUADRATICEXPONENTIAL)

        model = HestonModel(process)

        option = VanillaOption(c_payoff, exercise)

        engine = AnalyticHestonEngine(model, 144)

        option.set_pricing_engine(engine)

        price_fft = option.net_present_value

        engine = MCEuropeanHestonEngine(process,
                                        antithetic_variate=True,
                                        steps_per_year=nb_steps_a,
                                        required_samples=nb_paths,
                                        seed=seed)

        option.set_pricing_engine(engine)
        price_mc = option.net_present_value

        expected = 15.1796
        tolerance = .05

        self.assertAlmostEqual(price_fft, expected, delta=tolerance)
        self.assertAlmostEqual(price_mc, expected, delta=tolerance)
Ejemplo n.º 3
0
def heston_calibration(df_option, dtTrade=None, df_rates=None, ival=None):
    """
    calibrate heston model
    """

    # array of option helpers
    print df_option, df_rates, ival
    hh = heston_helpers(df_option, dtTrade, df_rates, ival)
    options = hh['options']
    spot = hh['spot']

    risk_free_ts = df_to_zero_curve(df_rates['R'], dtTrade)
    dividend_ts = df_to_zero_curve(df_rates['D'], dtTrade)

    if ival is None:
        ival = {
            'v0': 0.1,
            'kappa': 1.0,
            'theta': 0.1,
            'sigma': 0.5,
            'rho': -.5
        }

    process = HestonProcess(risk_free_ts, dividend_ts, spot, ival['v0'],
                            ival['kappa'], ival['theta'], ival['sigma'],
                            ival['rho'])

    model = HestonModel(process)
    engine = AnalyticHestonEngine(model, 64)

    for option in options:
        option.set_pricing_engine(engine)

    om = LevenbergMarquardt(1e-8, 1e-8, 1e-8)
    model.calibrate(options, om, EndCriteria(400, 40, 1.0e-8, 1.0e-8, 1.0e-8))

    print('model calibration results:')
    print('v0: %f kappa: %f theta: %f sigma: %f rho: %f' %
          (model.v0, model.kappa, model.theta, model.sigma, model.rho))

    calib_error = (1.0 / len(options)) * sum(
        [pow(o.calibration_error() * 100.0, 2) for o in options])

    print('SSE: %f' % calib_error)

    return merge_df(df_option, options, 'Heston')
Ejemplo n.º 4
0
def heston_calibration(df_option, ival=None):
    """
    calibrate heston model
    """

    tmp = make_helpers(df_option)

    risk_free_ts = tmp['risk_free_rate']
    dividend_ts = tmp['dividend_rate']
    spot = tmp['spot']
    options = tmp['options']

    # initial values for parameters
    if ival is None:
        ival = {
            'v0': 0.1,
            'kappa': 1.0,
            'theta': 0.1,
            'sigma': 0.5,
            'rho': -.5
        }

    process = HestonProcess(risk_free_ts, dividend_ts, spot, ival['v0'],
                            ival['kappa'], ival['theta'], ival['sigma'],
                            ival['rho'])

    model = HestonModel(process)
    engine = AnalyticHestonEngine(model, 64)

    for option in options:
        option.set_pricing_engine(engine)

    om = LevenbergMarquardt(1e-8, 1e-8, 1e-8)
    model.calibrate(options, om, EndCriteria(400, 40, 1.0e-8, 1.0e-8, 1.0e-8))

    print('model calibration results:')
    print('v0: %f kappa: %f theta: %f sigma: %f rho: %f' %
          (model.v0, model.kappa, model.theta, model.sigma, model.rho))

    calib_error = (1.0 / len(options)) * sum(
        [pow(o.calibration_error() * 100.0, 2) for o in options])

    print('SSE: %f' % calib_error)

    # merge the fitted volatility and the input data set
    return merge_df(df_option, options, 'Heston')
Ejemplo n.º 5
0
def heston_calibration(df_option, dtTrade=None, df_rates=None, ival=None):
    
    """
    calibrate heston model
    """

    # array of option helpers
    print df_option, df_rates, ival
    hh = heston_helpers(df_option, dtTrade, df_rates, ival)
    options = hh['options']
    spot = hh['spot']

    risk_free_ts = dfToZeroCurve(df_rates['R'], dtTrade)
    dividend_ts = dfToZeroCurve(df_rates['D'], dtTrade)

    if ival is None:
        ival = {'v0': 0.1, 'kappa': 1.0, 'theta': 0.1,
        'sigma': 0.5, 'rho': -.5}

    process = HestonProcess(
        risk_free_ts, dividend_ts, spot, ival['v0'], ival['kappa'],
         ival['theta'], ival['sigma'], ival['rho'])

    model = HestonModel(process)
    engine = AnalyticHestonEngine(model, 64)

    for option in options:
        option.set_pricing_engine(engine)

    om = LevenbergMarquardt(1e-8, 1e-8, 1e-8)
    model.calibrate(
        options, om, EndCriteria(400, 40, 1.0e-8, 1.0e-8, 1.0e-8)
    )

    print('model calibration results:')
    print('v0: %f kappa: %f theta: %f sigma: %f rho: %f' %
          (model.v0, model.kappa, model.theta, model.sigma,
           model.rho))

    calib_error = (1.0/len(options)) * sum(
        [pow(o.calibration_error()*100.0,2) for o in options])

    print('SSE: %f' % calib_error)

    return merge_df(df_option, options, 'Heston')
Ejemplo n.º 6
0
def heston_calibration(df_option, ival=None):
    """
    calibrate heston model
    """

    tmp = make_helpers(df_option)

    risk_free_ts = tmp['risk_free_rate']
    dividend_ts = tmp['dividend_rate']
    spot = tmp['spot']
    options = tmp['options']

    # initial values for parameters
    if ival is None:
        ival = {'v0': 0.1, 'kappa': 1.0, 'theta': 0.1,
                'sigma': 0.5, 'rho': -.5}

    process = HestonProcess(
        risk_free_ts, dividend_ts, spot, ival['v0'], ival['kappa'],
        ival['theta'], ival['sigma'], ival['rho'])

    model = HestonModel(process)
    engine = AnalyticHestonEngine(model, 64)

    for option in options:
        option.set_pricing_engine(engine)

    om = LevenbergMarquardt(1e-8, 1e-8, 1e-8)
    model.calibrate(
        options, om, EndCriteria(400, 40, 1.0e-8, 1.0e-8, 1.0e-8)
    )

    print('model calibration results:')
    print('v0: %f kappa: %f theta: %f sigma: %f rho: %f' %
          (model.v0, model.kappa, model.theta, model.sigma,
           model.rho))

    calib_error = (1.0 / len(options)) * sum(
        [pow(o.calibration_error() * 100.0, 2) for o in options])

    print('SSE: %f' % calib_error)

    # merge the fitted volatility and the input data set
    return merge_df(df_option, options, 'Heston')
Ejemplo n.º 7
0
    def test_analytic_versus_black(self):
        settlement_date = today()
        self.settings.evaluation_date = settlement_date

        daycounter = ActualActual()

        exercise_date = settlement_date + 6 * Months

        payoff = PlainVanillaPayoff(Put, 30)

        exercise = EuropeanExercise(exercise_date)

        risk_free_ts = flat_rate(0.1, daycounter)
        dividend_ts = flat_rate(0.04, daycounter)

        s0 = SimpleQuote(32.0)

        v0    = 0.05
        kappa = 5.0
        theta = 0.05
        sigma = 1.0e-4
        rho   = 0.0

        process = HestonProcess(
            risk_free_ts, dividend_ts, s0, v0, kappa, theta, sigma, rho
        )

        option = VanillaOption(payoff, exercise)

        engine = AnalyticHestonEngine(HestonModel(process), 144)

        option.set_pricing_engine(engine)

        calculated = option.net_present_value

        year_fraction = daycounter.year_fraction(
            settlement_date, exercise_date
        )

        forward_price = 32 * np.exp((0.1 - 0.04) * year_fraction)
        expected = blackFormula(
            payoff.type, payoff.strike, forward_price,
            np.sqrt(0.05 * year_fraction)
        ) * np.exp(-0.1 * year_fraction)

        tolerance = 2.0e-7

        self.assertAlmostEqual(
            calculated,
            expected,
            delta=tolerance
        )
Ejemplo n.º 8
0
def heston_pricer(trade_date, options, params, rates, spot):
    """
    Price a list of European options with heston model.

    """

    spot = SimpleQuote(spot)

    risk_free_ts = df_to_zero_curve(rates[nm.INTEREST_RATE], trade_date)
    dividend_ts = df_to_zero_curve(rates[nm.DIVIDEND_YIELD], trade_date)

    process = HestonProcess(risk_free_ts, dividend_ts, spot, **params)

    model = HestonModel(process)
    engine = AnalyticHestonEngine(model, 64)

    settlement_date = pydate_to_qldate(trade_date)

    settings = Settings()
    settings.evaluation_date = settlement_date

    modeled_values = np.zeros(len(options))

    for index, row in options.T.iteritems():

        expiry_date = row[nm.EXPIRY_DATE]
        strike = row[nm.STRIKE]

        option_type = Call if row[nm.OPTION_TYPE] == nm.CALL_OPTION else Put

        payoff = PlainVanillaPayoff(option_type, strike)

        expiry_qldate = pydate_to_qldate(expiry_date)
        exercise = EuropeanExercise(expiry_qldate)

        option = VanillaOption(payoff, exercise)
        option.set_pricing_engine(engine)

        modeled_values[index] = option.net_present_value

    prices = options.filter(
        items=[nm.EXPIRY_DATE, nm.STRIKE, nm.OPTION_TYPE, nm.SPOT])
    prices[nm.PRICE] = modeled_values
    prices[nm.TRADE_DATE] = trade_date

    return prices
Ejemplo n.º 9
0
    def test_simulate_heston_2(self):

        s0 = SimpleQuote(100.0)
        v0 = 0.05
        kappa = 5.0
        theta = 0.05
        sigma = 1.0e-4
        rho = 0.0

        process = HestonProcess(self.risk_free_ts, self.dividend_ts, s0, v0,
                                kappa, theta, sigma, rho)

        model = HestonModel(process)

        nbPaths = 4
        nbSteps = 100
        horizon = 1
        seed = 12345
        res = simulateHeston(model, nbPaths, nbSteps, horizon, seed)

        self.assertAlmostEqual(res[1, -1], 152.50, delta=.1)
Ejemplo n.º 10
0
    def test_simulate_heston_1(self):

        settings = self.settings
        settlement_date = today()
        settings.evaluation_date = settlement_date

        # simulate Heston paths
        paths = 4
        steps = 10
        horizon = 1
        seed = 12345

        model = HestonModel(self.heston_process)

        res = simulate_model(model, paths, steps, horizon, seed)

        time = res[0, :]
        time_expected = np.arange(0, 1.1, .1)
        simulations = res[1:, :].T

        np.testing.assert_array_almost_equal(time, time_expected, decimal=4)
Ejemplo n.º 11
0
    def test_heston_hw_calibration(self):
        """
        From Quantlib test suite
        """

        print("Testing Heston Hull-White calibration...")

        ## Calibration of a hybrid Heston-Hull-White model using
        ## the finite difference HestonHullWhite pricing engine
        ## Input surface is based on a Heston-Hull-White model with
        ## Hull-White: a = 0.00883, \sigma = 0.00631
        ## Heston    : \nu = 0.12, \kappa = 2.0,
        ##             \theta = 0.09, \sigma = 0.5, \rho=-0.75
        ## Equity Short rate correlation: -0.5

        dc = Actual365Fixed()
        calendar = TARGET()
        todays_date = Date(28, March, 2004)
        settings = Settings()
        settings.evaluation_date = todays_date

        r_ts = flat_rate(todays_date, 0.05, dc)

        ## assuming, that the Hull-White process is already calibrated
        ## on a given set of pure interest rate calibration instruments.

        hw_process = HullWhiteProcess(r_ts, a=0.00883, sigma=0.00631)

        q_ts = flat_rate(todays_date, 0.02, dc)
        s0 = SimpleQuote(100.0)

        # vol surface

        strikes = [50, 75, 90, 100, 110, 125, 150, 200]
        maturities = [1 / 12., 3 / 12., 0.5, 1.0, 2.0, 3.0, 5.0, 7.5, 10]

        vol = [
            0.482627, 0.407617, 0.366682, 0.340110, 0.314266, 0.280241,
            0.252471, 0.325552, 0.464811, 0.393336, 0.354664, 0.329758,
            0.305668, 0.273563, 0.244024, 0.244886, 0.441864, 0.375618,
            0.340464, 0.318249, 0.297127, 0.268839, 0.237972, 0.225553,
            0.407506, 0.351125, 0.322571, 0.305173, 0.289034, 0.267361,
            0.239315, 0.213761, 0.366761, 0.326166, 0.306764, 0.295279,
            0.284765, 0.270592, 0.250702, 0.222928, 0.345671, 0.314748,
            0.300259, 0.291744, 0.283971, 0.273475, 0.258503, 0.235683,
            0.324512, 0.303631, 0.293981, 0.288338, 0.283193, 0.276248,
            0.266271, 0.250506, 0.311278, 0.296340, 0.289481, 0.285482,
            0.281840, 0.276924, 0.269856, 0.258609, 0.303219, 0.291534,
            0.286187, 0.283073, 0.280239, 0.276414, 0.270926, 0.262173
        ]

        start_v0 = 0.2 * 0.2
        start_theta = start_v0
        start_kappa = 0.5
        start_sigma = 0.25
        start_rho = -0.5

        equityShortRateCorr = -0.5

        corrConstraint = HestonHullWhiteCorrelationConstraint(
            equityShortRateCorr)

        heston_process = HestonProcess(r_ts, q_ts, s0, start_v0, start_kappa,
                                       start_theta, start_sigma, start_rho)

        h_model = HestonModel(heston_process)
        h_engine = AnalyticHestonEngine(h_model)

        options = []

        # first calibrate a heston model to get good initial
        # parameters

        for i in range(len(maturities)):
            maturity = Period(int(maturities[i] * 12.0 + 0.5), Months)

            for j, s in enumerate(strikes):

                v = SimpleQuote(vol[i * len(strikes) + j])

                helper = HestonModelHelper(maturity, calendar, s0.value, s, v,
                                           r_ts, q_ts, PriceError)

                helper.set_pricing_engine(h_engine)

                options.append(helper)

        om = LevenbergMarquardt(1e-6, 1e-8, 1e-8)

        # Heston model
        h_model.calibrate(options, om,
                          EndCriteria(400, 40, 1.0e-8, 1.0e-4, 1.0e-8))

        print("Heston calibration")
        print("v0: %f" % h_model.v0)
        print("theta: %f" % h_model.theta)
        print("kappa: %f" % h_model.kappa)
        print("sigma: %f" % h_model.sigma)
        print("rho: %f" % h_model.rho)

        h_process_2 = HestonProcess(r_ts, q_ts, s0, h_model.v0, h_model.kappa,
                                    h_model.theta, h_model.sigma, h_model.rho)

        hhw_model = HestonModel(h_process_2)

        options = []
        for i in range(len(maturities)):

            tGrid = np.max((10.0, maturities[i] * 10.0))
            hhw_engine = FdHestonHullWhiteVanillaEngine(
                hhw_model, hw_process, equityShortRateCorr, tGrid, 61, 13, 9,
                0, True, FdmSchemeDesc.Hundsdorfer())

            hhw_engine.enable_multiple_strikes_caching(strikes)

            maturity = Period(int(maturities[i] * 12.0 + 0.5), Months)

            # multiple strikes engine works best if the first option
            # per maturity has the average strike (because the first
            # option is priced first during the calibration and
            # the first pricing is used to calculate the prices
            # for all strikes

            # list of strikes by distance from moneyness

            indx = np.argsort(np.abs(np.array(strikes) - s0.value))

            for j, tmp in enumerate(indx):
                js = indx[j]
                s = strikes[js]
                v = SimpleQuote(vol[i * len(strikes) + js])
                helper = HestonModelHelper(maturity, calendar, s0.value,
                                           strikes[js], v, r_ts, q_ts,
                                           PriceError)
                helper.set_pricing_engine(hhw_engine)
                options.append(helper)

        vm = LevenbergMarquardt(1e-6, 1e-2, 1e-2)

        hhw_model.calibrate(options, vm,
                            EndCriteria(400, 40, 1.0e-8, 1.0e-4, 1.0e-8),
                            corrConstraint)

        print("Heston HW calibration with FD engine")
        print("v0: %f" % hhw_model.v0)
        print("theta: %f" % hhw_model.theta)
        print("kappa: %f" % hhw_model.kappa)
        print("sigma: %f" % hhw_model.sigma)
        print("rho: %f" % hhw_model.rho)

        relTol = 0.05
        expected_v0 = 0.12
        expected_kappa = 2.0
        expected_theta = 0.09
        expected_sigma = 0.5
        expected_rho = -0.75

        self.assertAlmostEquals(np.abs(hhw_model.v0 - expected_v0) /
                                expected_v0,
                                0,
                                delta=relTol)

        self.assertAlmostEquals(np.abs(hhw_model.theta - expected_theta) /
                                expected_theta,
                                0,
                                delta=relTol)

        self.assertAlmostEquals(np.abs(hhw_model.kappa - expected_kappa) /
                                expected_kappa,
                                0,
                                delta=relTol)

        self.assertAlmostEquals(np.abs(hhw_model.sigma - expected_sigma) /
                                expected_sigma,
                                0,
                                delta=relTol)

        self.assertAlmostEquals(np.abs(hhw_model.rho - expected_rho) /
                                expected_rho,
                                0,
                                delta=relTol)
Ejemplo n.º 12
0
    def test_black_calibration(self):

        # calibrate a Heston model to a constant volatility surface without
        # smile. expected result is a vanishing volatility of the volatility.
        # In addition theta and v0 should be equal to the constant variance

        todays_date = today()

        self.settings.evaluation_date = todays_date

        daycounter = Actual360()
        calendar = NullCalendar()

        risk_free_ts = flat_rate(0.04, daycounter)
        dividend_ts = flat_rate(0.50, daycounter)

        option_maturities = [
            Period(1, Months),
            Period(2, Months),
            Period(3, Months),
            Period(6, Months),
            Period(9, Months),
            Period(1, Years),
            Period(2, Years)
        ]

        options = []

        s0 = SimpleQuote(1.0)
        vol = SimpleQuote(0.1)

        volatility = vol.value

        for maturity in option_maturities:
            for moneyness in np.arange(-1.0, 2.0, 1.):
                tau = daycounter.year_fraction(
                    risk_free_ts.reference_date,
                    calendar.advance(
                        risk_free_ts.reference_date,
                        period=maturity)
                )
                forward_price = s0.value * dividend_ts.discount(tau) / \
                                risk_free_ts.discount(tau)
                strike_price = forward_price * np.exp(
                    -moneyness * volatility * np.sqrt(tau)
                )
                options.append(
                    HestonModelHelper(
                        maturity, calendar, s0.value, strike_price, vol,
                        risk_free_ts, dividend_ts
                    )
                )

        for sigma in np.arange(0.1, 0.7, 0.2):
            v0    = 0.01
            kappa = 0.2
            theta = 0.02
            rho   = -0.75

            process = HestonProcess(
                risk_free_ts, dividend_ts, s0, v0, kappa, theta, sigma, rho
            )

            self.assertEqual(v0, process.v0)
            self.assertEqual(kappa, process.kappa)
            self.assertEqual(theta, process.theta)
            self.assertEqual(sigma, process.sigma)
            self.assertEqual(rho, process.rho)
            self.assertEqual(1.0, process.s0().value)

            model = HestonModel(process)
            engine = AnalyticHestonEngine(model, 96)

            for option in options:
                option.set_pricing_engine(engine)

            optimisation_method = LevenbergMarquardt(1e-8, 1e-8, 1e-8)

            end_criteria = EndCriteria(400, 40, 1.0e-8, 1.0e-8, 1.0e-8)
            model.calibrate(options, optimisation_method, end_criteria)

            tolerance = 3.0e-3

            self.assertFalse(model.sigma > tolerance)

            self.assertAlmostEqual(
                model.kappa * model.theta,
                model.kappa * volatility ** 2,
                delta=tolerance
            )
            self.assertAlmostEqual(model.v0, volatility ** 2, delta=tolerance)
Ejemplo n.º 13
0
    def test_DAX_calibration(self):

        # this example is taken from A. Sepp
        # Pricing European-Style Options under Jump Diffusion Processes
        # with Stochstic Volatility: Applications of Fourier Transform
        # http://math.ut.ee/~spartak/papers/stochjumpvols.pdf

        settlement_date = Date(5, July, 2002)

        self.settings.evaluation_date = settlement_date

        daycounter = Actual365Fixed()
        calendar = TARGET()

        t = [13, 41, 75, 165, 256, 345, 524, 703]
        r = [0.0357,0.0349,0.0341,0.0355,0.0359,0.0368,0.0386,0.0401]

        dates = [settlement_date] + [settlement_date + val for val in t]
        rates = [0.0357] + r

        risk_free_ts = ZeroCurve(dates, rates, daycounter)
        dividend_ts = FlatForward(
            settlement_date, forward=0.0, daycounter=daycounter
        )

        v = [
            0.6625,0.4875,0.4204,0.3667,0.3431,0.3267,0.3121,0.3121,
            0.6007,0.4543,0.3967,0.3511,0.3279,0.3154,0.2984,0.2921,
            0.5084,0.4221,0.3718,0.3327,0.3155,0.3027,0.2919,0.2889,
            0.4541,0.3869,0.3492,0.3149,0.2963,0.2926,0.2819,0.2800,
            0.4060,0.3607,0.3330,0.2999,0.2887,0.2811,0.2751,0.2775,
            0.3726,0.3396,0.3108,0.2781,0.2788,0.2722,0.2661,0.2686,
            0.3550,0.3277,0.3012,0.2781,0.2781,0.2661,0.2661,0.2681,
            0.3428,0.3209,0.2958,0.2740,0.2688,0.2627,0.2580,0.2620,
            0.3302,0.3062,0.2799,0.2631,0.2573,0.2533,0.2504,0.2544,
            0.3343,0.2959,0.2705,0.2540,0.2504,0.2464,0.2448,0.2462,
            0.3460,0.2845,0.2624,0.2463,0.2425,0.2385,0.2373,0.2422,
            0.3857,0.2860,0.2578,0.2399,0.2357,0.2327,0.2312,0.2351,
            0.3976,0.2860,0.2607,0.2356,0.2297,0.2268,0.2241,0.2320
        ]

        s0 = SimpleQuote(4468.17)
        strikes = [
            3400, 3600, 3800, 4000, 4200, 4400, 4500, 4600, 4800, 5000, 5200,
            5400, 5600
        ]

        options = []

        for s, strike in enumerate(strikes):
            for m in range(len(t)):
                vol = SimpleQuote(v[s * 8 + m])
                # round to weeks
                maturity = Period((int)((t[m] + 3) / 7.), Weeks)
                options.append(
                    HestonModelHelper(
                        maturity, calendar, s0.value, strike, vol,
                        risk_free_ts, dividend_ts,
                        ImpliedVolError
                    )
                )

        v0    = 0.1
        kappa = 1.0
        theta = 0.1
        sigma = 0.5
        rho   = -0.5

        process = HestonProcess(
            risk_free_ts, dividend_ts, s0, v0, kappa, theta, sigma, rho
        )

        model = HestonModel(process)

        engine = AnalyticHestonEngine(model, 64)

        for option in options:
            option.set_pricing_engine(engine)

        om = LevenbergMarquardt(1e-8, 1e-8, 1e-8)

        model.calibrate(
            options, om, EndCriteria(400, 40, 1.0e-8, 1.0e-8, 1.0e-8)
        )

        sse = 0
        for i in range(len(strikes) * len(t)):
            diff = options[i].calibration_error() * 100.0
            sse += diff * diff

        expected = 177.2  # see article by A. Sepp.
        self.assertAlmostEqual(expected, sse, delta=1.0)
Ejemplo n.º 14
0
def heston_calibration(df_option, ival=None):
    """
    calibrate heston model
    """

    # extract rates and div yields from the data set
    df_tmp = DataFrame.filter(df_option, items=['dtExpiry', 'iRate', 'iDiv'])
    grouped = df_tmp.groupby('dtExpiry')
    df_rates = grouped.agg(lambda x: x[0])

    dtTrade = df_option['dtTrade'][0]
    # back out the spot from any forward
    iRate = df_option['iRate'][0]
    iDiv = df_option['iDiv'][0]
    TTM = df_option['TTM'][0]
    Fwd = df_option['Fwd'][0]
    spot = SimpleQuote(Fwd * np.exp(-(iRate - iDiv) * TTM))
    print('Spot: %f risk-free rate: %f div. yield: %f' %
          (spot.value, iRate, iDiv))

    # build array of option helpers
    hh = heston_helpers(spot, df_option, dtTrade, df_rates)
    options = hh['options']
    spot = hh['spot']

    risk_free_ts = dfToZeroCurve(df_rates['iRate'], dtTrade)
    dividend_ts = dfToZeroCurve(df_rates['iDiv'], dtTrade)

    # initial values for parameters
    if ival is None:
        ival = {
            'v0': 0.1,
            'kappa': 1.0,
            'theta': 0.1,
            'sigma': 0.5,
            'rho': -.5
        }

    process = HestonProcess(risk_free_ts, dividend_ts, spot, ival['v0'],
                            ival['kappa'], ival['theta'], ival['sigma'],
                            ival['rho'])

    model = HestonModel(process)
    engine = AnalyticHestonEngine(model, 64)

    for option in options:
        option.set_pricing_engine(engine)

    om = LevenbergMarquardt(1e-8, 1e-8, 1e-8)
    model.calibrate(options, om, EndCriteria(400, 40, 1.0e-8, 1.0e-8, 1.0e-8))

    print('model calibration results:')
    print('v0: %f kappa: %f theta: %f sigma: %f rho: %f' %
          (model.v0, model.kappa, model.theta, model.sigma, model.rho))

    calib_error = (1.0 / len(options)) * sum(
        [pow(o.calibration_error() * 100.0, 2) for o in options])

    print('SSE: %f' % calib_error)

    # merge the fitted volatility and the input data set
    return merge_df(df_option, options, 'Heston')
Ejemplo n.º 15
0
    def test_heston_hw_calibration(self):
        """
        From Quantlib test suite
        """

        print("Testing Heston Hull-White calibration...")

        ## Calibration of a hybrid Heston-Hull-White model using
        ## the finite difference HestonHullWhite pricing engine
        ## Input surface is based on a Heston-Hull-White model with
        ## Hull-White: a = 0.00883, \sigma = 0.00631
        ## Heston    : \nu = 0.12, \kappa = 2.0,
        ##             \theta = 0.09, \sigma = 0.5, \rho=-0.75
        ## Equity Short rate correlation: -0.5

        dc = Actual365Fixed()
        calendar = TARGET()
        todays_date = Date(28, March, 2004)
        settings = Settings()
        settings.evaluation_date = todays_date

        r_ts = flat_rate(todays_date, 0.05, dc)

        ## assuming, that the Hull-White process is already calibrated
        ## on a given set of pure interest rate calibration instruments.

        hw_process = HullWhiteProcess(r_ts, a=0.00883, sigma=0.00631)

        q_ts = flat_rate(todays_date, 0.02, dc)
        s0 = SimpleQuote(100.0)

        # vol surface

        strikes    = [50, 75, 90, 100, 110, 125, 150, 200]
        maturities = [1 / 12., 3 / 12., 0.5, 1.0, 2.0, 3.0, 5.0, 7.5, 10]

        vol = [
        0.482627,0.407617,0.366682,0.340110,0.314266,0.280241,0.252471,0.325552,
        0.464811,0.393336,0.354664,0.329758,0.305668,0.273563,0.244024,0.244886,
        0.441864,0.375618,0.340464,0.318249,0.297127,0.268839,0.237972,0.225553,
        0.407506,0.351125,0.322571,0.305173,0.289034,0.267361,0.239315,0.213761,
        0.366761,0.326166,0.306764,0.295279,0.284765,0.270592,0.250702,0.222928,
        0.345671,0.314748,0.300259,0.291744,0.283971,0.273475,0.258503,0.235683,
        0.324512,0.303631,0.293981,0.288338,0.283193,0.276248,0.266271,0.250506,
        0.311278,0.296340,0.289481,0.285482,0.281840,0.276924,0.269856,0.258609,
        0.303219,0.291534,0.286187,0.283073,0.280239,0.276414,0.270926,0.262173]

        start_v0    = 0.2 * 0.2
        start_theta = start_v0
        start_kappa = 0.5
        start_sigma = 0.25
        start_rho   = -0.5

        equityShortRateCorr = -0.5

        corrConstraint = HestonHullWhiteCorrelationConstraint(
            equityShortRateCorr)

        heston_process = HestonProcess(r_ts, q_ts, s0, start_v0, start_kappa,
                                       start_theta, start_sigma, start_rho)

        h_model = HestonModel(heston_process)
        h_engine = AnalyticHestonEngine(h_model)

        options = []

        # first calibrate a heston model to get good initial
        # parameters

        for i in range(len(maturities)):
            maturity = Period(int(maturities[i] * 12.0 + 0.5), Months)

            for j, s in enumerate(strikes):

                v = SimpleQuote(vol[i * len(strikes) + j])

                helper = HestonModelHelper(maturity, calendar, s0.value,
                                           s, v, r_ts, q_ts,
                                           PriceError)

                helper.set_pricing_engine(h_engine)

                options.append(helper)

        om = LevenbergMarquardt(1e-6, 1e-8, 1e-8)

        # Heston model
        h_model.calibrate(options, om,
                          EndCriteria(400, 40, 1.0e-8,
                                      1.0e-4, 1.0e-8))

        print("Heston calibration")
        print("v0: %f" % h_model.v0)
        print("theta: %f" % h_model.theta)
        print("kappa: %f" % h_model.kappa)
        print("sigma: %f" % h_model.sigma)
        print("rho: %f" % h_model.rho)

        h_process_2 = HestonProcess(r_ts, q_ts, s0, h_model.v0,
                                    h_model.kappa,
                                    h_model.theta,
                                    h_model.sigma,
                                    h_model.rho)

        hhw_model = HestonModel(h_process_2)

        options = []
        for i in range(len(maturities)):

            tGrid = np.max((10.0, maturities[i] * 10.0))
            hhw_engine = FdHestonHullWhiteVanillaEngine(
                hhw_model, hw_process,
                equityShortRateCorr,
                tGrid, 61, 13, 9, 0, True, FdmSchemeDesc.Hundsdorfer())

            hhw_engine.enable_multiple_strikes_caching(strikes)

            maturity = Period(int(maturities[i] * 12.0 + 0.5), Months)

            # multiple strikes engine works best if the first option
            # per maturity has the average strike (because the first
            # option is priced first during the calibration and
            # the first pricing is used to calculate the prices
            # for all strikes

            # list of strikes by distance from moneyness

            indx = np.argsort(np.abs(np.array(strikes) - s0.value))

            for j, tmp in enumerate(indx):
                js = indx[j]
                s = strikes[js]
                v = SimpleQuote(vol[i * len(strikes) + js])
                helper = HestonModelHelper(maturity,
                                           calendar, s0.value,
                                           strikes[js], v, r_ts, q_ts,
                                           PriceError)
                helper.set_pricing_engine(hhw_engine)
                options.append(helper)

        vm = LevenbergMarquardt(1e-6, 1e-2, 1e-2)

        hhw_model.calibrate(options, vm,
                            EndCriteria(400, 40, 1.0e-8, 1.0e-4, 1.0e-8),
                            corrConstraint)

        print("Heston HW calibration with FD engine")
        print("v0: %f" % hhw_model.v0)
        print("theta: %f" % hhw_model.theta)
        print("kappa: %f" % hhw_model.kappa)
        print("sigma: %f" % hhw_model.sigma)
        print("rho: %f" % hhw_model.rho)

        relTol = 0.05
        expected_v0 = 0.12
        expected_kappa = 2.0
        expected_theta = 0.09
        expected_sigma = 0.5
        expected_rho = -0.75

        self.assertAlmostEqual(
            np.abs(hhw_model.v0 - expected_v0) / expected_v0, 0,
            delta=relTol)

        self.assertAlmostEqual(
            np.abs(hhw_model.theta - expected_theta) / expected_theta, 0,
            delta=relTol)

        self.assertAlmostEqual(
            np.abs(hhw_model.kappa - expected_kappa) / expected_kappa, 0,
            delta=relTol)

        self.assertAlmostEqual(
            np.abs(hhw_model.sigma - expected_sigma) / expected_sigma, 0,
            delta=relTol)

        self.assertAlmostEqual(
            np.abs(hhw_model.rho - expected_rho) / expected_rho, 0,
            delta=relTol)
Ejemplo n.º 16
0
# The simulation
# --------------
# 
# The *simulate* function is not part of Quantlib. It has been added
# to the pyQL interface (see folder quantlib/sim). This illustrates
# how to create extensions to Quantlib and expose them to python.

# <codecell>

import pylab as pl
from quantlib.sim.simulate import simulateHeston

# simulate and plot Heston paths
paths = 20
steps = 100
horizon = 2
seed = 12345

model = HestonModel(process)

res = simulateHeston(model, paths, steps, horizon, seed)

time = res[0, :]
simulations = res[1:, :].T
pl.plot(time, simulations)
pl.xlabel('Time')
pl.ylabel('Stock Price')
pl.title('Heston Process Simulation')
pl.show()
Ejemplo n.º 17
0
v0 = .1
kappa_v = 2
theta_v = 0.1
sigma_v = 0.3
rho_sv = -0.5

hestonProcess = HestonProcess(risk_free_rate_ts=r_ts,
                              dividend_ts=q_ts,
                              s0=s0,
                              v0=v0,
                              kappa=kappa_v,
                              theta=theta_v,
                              sigma=sigma_v,
                              rho=rho_sv)

hestonModel = HestonModel(hestonProcess)

# Hull-White

kappa_r = 1
sigma_r = .2

hullWhiteProcess = HullWhiteProcess(r_ts, a=kappa_r, sigma=sigma_r)

strike = 100
maturity = 1
type = Call

maturity_date = todays_date + Period(maturity, Years)

exercise = EuropeanExercise(maturity_date)
Ejemplo n.º 18
0
    def test_compare_bsm_bsmhw_hestonhw(self):

        dc = Actual365Fixed()

        todays_date = today()
        settings = Settings()
        settings.evaluation_date = todays_date
        tol = 1.e-2

        spot = SimpleQuote(100)

        dates = [todays_date + Period(i, Years) for i in range(40)]

        rates = [0.01 + 0.0002 * np.exp(np.sin(i / 4.0)) for i in range(40)]
        divRates = [0.02 + 0.0001 * np.exp(np.sin(i / 5.0)) for i in range(40)]

        s0 = SimpleQuote(100)

        r_ts = ZeroCurve(dates, rates, dc)
        q_ts = ZeroCurve(dates, divRates, dc)

        vol = SimpleQuote(0.25)
        vol_ts = BlackConstantVol(todays_date, NullCalendar(), vol.value, dc)

        bsm_process = BlackScholesMertonProcess(spot, q_ts, r_ts, vol_ts)

        payoff = PlainVanillaPayoff(Call, 100)
        exercise = EuropeanExercise(dates[1])

        option = VanillaOption(payoff, exercise)

        analytic_european_engine = AnalyticEuropeanEngine(bsm_process)

        option.set_pricing_engine(analytic_european_engine)
        npv_bsm = option.npv

        variance = vol.value * vol.value
        hestonProcess = HestonProcess(risk_free_rate_ts=r_ts,
                                      dividend_ts=q_ts,
                                      s0=s0,
                                      v0=variance,
                                      kappa=5.0,
                                      theta=variance,
                                      sigma=1e-4,
                                      rho=0.0)

        hestonModel = HestonModel(hestonProcess)

        hullWhiteModel = HullWhite(r_ts, a=0.01, sigma=0.01)

        bsmhwEngine = AnalyticBSMHullWhiteEngine(0.0, bsm_process,
                                                 hullWhiteModel)

        hestonHwEngine = AnalyticHestonHullWhiteEngine(hestonModel,
                                                       hullWhiteModel, 128)

        hestonEngine = AnalyticHestonEngine(hestonModel, 144)
        option.set_pricing_engine(hestonEngine)

        npv_heston = option.npv

        option.set_pricing_engine(bsmhwEngine)
        npv_bsmhw = option.npv

        option.set_pricing_engine(hestonHwEngine)
        npv_hestonhw = option.npv

        print("calculated with BSM: %f" % npv_bsm)
        print("BSM-HW: %f" % npv_bsmhw)
        print("Heston: %f" % npv_heston)
        print("Heston-HW: %f" % npv_hestonhw)

        self.assertAlmostEqual(npv_bsm, npv_bsmhw, delta=tol)
        self.assertAlmostEqual(npv_bsm, npv_hestonhw, delta=tol)
Ejemplo n.º 19
0
    def test_DAX_calibration(self):

        # this example is taken from A. Sepp
        # Pricing European-Style Options under Jump Diffusion Processes
        # with Stochstic Volatility: Applications of Fourier Transform
        # http://math.ut.ee/~spartak/papers/stochjumpvols.pdf

        settlement_date = Date(5, July, 2002)

        self.settings.evaluation_date = settlement_date

        daycounter = Actual365Fixed()
        calendar = TARGET()

        t = [13, 41, 75, 165, 256, 345, 524, 703]
        r = [0.0357,0.0349,0.0341,0.0355,0.0359,0.0368,0.0386,0.0401]

        dates = [settlement_date] + [settlement_date + val for val in t]
        rates = [0.0357] + r

        risk_free_ts = ZeroCurve(dates, rates, daycounter)
        dividend_ts = FlatForward(
            settlement_date, forward=0.0, daycounter=daycounter
        )

        v = [
            0.6625,0.4875,0.4204,0.3667,0.3431,0.3267,0.3121,0.3121,
            0.6007,0.4543,0.3967,0.3511,0.3279,0.3154,0.2984,0.2921,
            0.5084,0.4221,0.3718,0.3327,0.3155,0.3027,0.2919,0.2889,
            0.4541,0.3869,0.3492,0.3149,0.2963,0.2926,0.2819,0.2800,
            0.4060,0.3607,0.3330,0.2999,0.2887,0.2811,0.2751,0.2775,
            0.3726,0.3396,0.3108,0.2781,0.2788,0.2722,0.2661,0.2686,
            0.3550,0.3277,0.3012,0.2781,0.2781,0.2661,0.2661,0.2681,
            0.3428,0.3209,0.2958,0.2740,0.2688,0.2627,0.2580,0.2620,
            0.3302,0.3062,0.2799,0.2631,0.2573,0.2533,0.2504,0.2544,
            0.3343,0.2959,0.2705,0.2540,0.2504,0.2464,0.2448,0.2462,
            0.3460,0.2845,0.2624,0.2463,0.2425,0.2385,0.2373,0.2422,
            0.3857,0.2860,0.2578,0.2399,0.2357,0.2327,0.2312,0.2351,
            0.3976,0.2860,0.2607,0.2356,0.2297,0.2268,0.2241,0.2320
        ]

        s0 = SimpleQuote(4468.17)
        strikes = [
            3400, 3600, 3800, 4000, 4200, 4400, 4500, 4600, 4800, 5000, 5200,
            5400, 5600
        ]

        options = []

        for s, strike in enumerate(strikes):
            for m in range(len(t)):
                vol = SimpleQuote(v[s * 8 + m])
                # round to weeks
                maturity = Period((int)((t[m] + 3) / 7.), Weeks)
                options.append(
                    HestonModelHelper(
                        maturity, calendar, s0.value, strike, vol,
                        risk_free_ts, dividend_ts,
                        ImpliedVolError
                    )
                )

        v0    = 0.1
        kappa = 1.0
        theta = 0.1
        sigma = 0.5
        rho   = -0.5

        process = HestonProcess(
            risk_free_ts, dividend_ts, s0, v0, kappa, theta, sigma, rho
        )

        model = HestonModel(process)

        engine = AnalyticHestonEngine(model, 64)

        for option in options:
            option.set_pricing_engine(engine)

        om = LevenbergMarquardt(1e-8, 1e-8, 1e-8)

        model.calibrate(
            options, om, EndCriteria(400, 40, 1.0e-8, 1.0e-8, 1.0e-8)
        )

        sse = 0
        for i in range(len(strikes) * len(t)):
            diff = options[i].calibration_error() * 100.0
            sse += diff * diff

        expected = 177.2  # see article by A. Sepp.
        self.assertAlmostEqual(expected, sse, delta=1.0)
Ejemplo n.º 20
0
    def test_zanette(self):
        """
        From paper by A. Zanette et al.
        """

        dc = Actual365Fixed()

        todays_date = today()
        settings = Settings()
        settings.evaluation_date = todays_date

        # constant yield and div curves

        dates = [todays_date + Period(i, Years) for i in range(3)]
        rates = [0.04 for i in range(3)]
        divRates = [0.03 for i in range(3)]
        r_ts = ZeroCurve(dates, rates, dc)
        q_ts = ZeroCurve(dates, divRates, dc)

        s0 = SimpleQuote(100)

        # Heston model

        v0 = .1
        kappa_v = 2
        theta_v = 0.1
        sigma_v = 0.3
        rho_sv = -0.5

        hestonProcess = HestonProcess(risk_free_rate_ts=r_ts,
                                      dividend_ts=q_ts,
                                      s0=s0,
                                      v0=v0,
                                      kappa=kappa_v,
                                      theta=theta_v,
                                      sigma=sigma_v,
                                      rho=rho_sv)

        hestonModel = HestonModel(hestonProcess)

        # Hull-White

        kappa_r = 1
        sigma_r = .2

        hullWhiteProcess = HullWhiteProcess(r_ts, a=kappa_r, sigma=sigma_r)

        strike = 100
        maturity = 1
        type = Call

        maturity_date = todays_date + Period(maturity, Years)

        exercise = EuropeanExercise(maturity_date)

        payoff = PlainVanillaPayoff(type, strike)

        option = VanillaOption(payoff, exercise)

        def price_cal(rho, tGrid):
            fd_hestonHwEngine = FdHestonHullWhiteVanillaEngine(
                hestonModel, hullWhiteProcess, rho, tGrid, 100, 40, 20, 0,
                True, FdmSchemeDesc.Hundsdorfer())
            option.set_pricing_engine(fd_hestonHwEngine)
            return option.npv

        calc_price = []
        for rho in [-0.5, 0, .5]:
            for tGrid in [50, 100, 150, 200]:
                tmp = price_cal(rho, tGrid)
                print("rho (S,r): %f Ns: %d Price: %f" % (rho, tGrid, tmp))
                calc_price.append(tmp)

        expected_price = [
            11.38,
        ] * 4 + [
            12.79,
        ] * 4 + [
            14.06,
        ] * 4

        np.testing.assert_almost_equal(calc_price, expected_price, 2)
Ejemplo n.º 21
0
    def test_compare_BsmHW_HestonHW(self):
        """
        From Quantlib test suite
        """

        print("Comparing European option pricing for a BSM " +
              "process with one-factor Hull-White model...")

        dc = Actual365Fixed()

        todays_date = today()
        settings = Settings()
        settings.evaluation_date = todays_date
        tol = 1.e-2

        spot = SimpleQuote(100)

        dates = [todays_date + Period(i, Years) for i in range(40)]

        rates = [0.01 + 0.0002 * np.exp(np.sin(i / 4.0)) for i in range(40)]
        divRates = [0.02 + 0.0001 * np.exp(np.sin(i / 5.0)) for i in range(40)]

        s0 = SimpleQuote(100)

        r_ts = ZeroCurve(dates, rates, dc)
        q_ts = ZeroCurve(dates, divRates, dc)

        vol = SimpleQuote(0.25)
        vol_ts = BlackConstantVol(todays_date, NullCalendar(), vol.value, dc)

        bsm_process = BlackScholesMertonProcess(spot, q_ts, r_ts, vol_ts)

        variance = vol.value * vol.value
        hestonProcess = HestonProcess(risk_free_rate_ts=r_ts,
                                      dividend_ts=q_ts,
                                      s0=s0,
                                      v0=variance,
                                      kappa=5.0,
                                      theta=variance,
                                      sigma=1e-4,
                                      rho=0.0)

        hestonModel = HestonModel(hestonProcess)

        hullWhiteModel = HullWhite(r_ts, a=0.01, sigma=0.01)

        bsmhwEngine = AnalyticBSMHullWhiteEngine(0.0, bsm_process,
                                                 hullWhiteModel)

        hestonHwEngine = AnalyticHestonHullWhiteEngine(hestonModel,
                                                       hullWhiteModel, 128)

        tol = 1e-5
        strikes = [0.25, 0.5, 0.75, 0.8, 0.9, 1.0, 1.1, 1.2, 1.5, 2.0, 4.0]
        maturities = [1, 2, 3, 5, 10, 15, 20, 25, 30]
        types = [Put, Call]

        for type in types:
            for strike in strikes:
                for maturity in maturities:
                    maturity_date = todays_date + Period(maturity, Years)

                    exercise = EuropeanExercise(maturity_date)

                    fwd = strike * s0.value * \
                        q_ts.discount(maturity_date) / \
                        r_ts.discount(maturity_date)

                    payoff = PlainVanillaPayoff(type, fwd)

                    option = VanillaOption(payoff, exercise)

                    option.set_pricing_engine(bsmhwEngine)
                    calculated = option.npv

                    option.set_pricing_engine(hestonHwEngine)
                    expected = option.npv

                    if ((np.abs(expected - calculated) > calculated * tol)
                            and (np.abs(expected - calculated) > tol)):

                        cp = PAYOFF_TO_STR[type]
                        print("Failed to reproduce npv")
                        print("strike    : %f" % strike)
                        print("maturity  : %d" % maturity)
                        print("type      : %s" % cp)

                    self.assertAlmostEqual(expected, calculated, delta=tol)
Ejemplo n.º 22
0
    def test_black_calibration(self):

        # calibrate a Heston model to a constant volatility surface without
        # smile. expected result is a vanishing volatility of the volatility.
        # In addition theta and v0 should be equal to the constant variance

        todays_date = today()

        self.settings.evaluation_date = todays_date

        daycounter = Actual360()
        calendar = NullCalendar()

        risk_free_ts = flat_rate(0.04, daycounter)
        dividend_ts = flat_rate(0.50, daycounter)

        option_maturities = [
            Period(1, Months),
            Period(2, Months),
            Period(3, Months),
            Period(6, Months),
            Period(9, Months),
            Period(1, Years),
            Period(2, Years)
        ]

        options = []

        s0 = SimpleQuote(1.0)
        vol = SimpleQuote(0.1)

        volatility = vol.value

        for maturity in option_maturities:
            for moneyness in np.arange(-1.0, 2.0, 1.):
                tau = daycounter.year_fraction(
                    risk_free_ts.reference_date,
                    calendar.advance(
                        risk_free_ts.reference_date,
                        period=maturity)
                )
                forward_price = s0.value * dividend_ts.discount(tau) / \
                                risk_free_ts.discount(tau)
                strike_price = forward_price * np.exp(
                    -moneyness * volatility * np.sqrt(tau)
                )
                options.append(
                    HestonModelHelper(
                        maturity, calendar, s0.value, strike_price, vol,
                        risk_free_ts, dividend_ts
                    )
                )

        for sigma in np.arange(0.1, 0.7, 0.2):
            v0    = 0.01
            kappa = 0.2
            theta = 0.02
            rho   = -0.75

            process = HestonProcess(
                risk_free_ts, dividend_ts, s0, v0, kappa, theta, sigma, rho
            )

            self.assertEqual(v0, process.v0)
            self.assertEqual(kappa, process.kappa)
            self.assertEqual(theta, process.theta)
            self.assertEqual(sigma, process.sigma)
            self.assertEqual(rho, process.rho)
            self.assertEqual(1.0, process.s0.value)

            model = HestonModel(process)
            engine = AnalyticHestonEngine(model, 96)

            for option in options:
                option.set_pricing_engine(engine)

            optimisation_method = LevenbergMarquardt(1e-8, 1e-8, 1e-8)

            end_criteria = EndCriteria(400, 40, 1.0e-8, 1.0e-8, 1.0e-8)
            model.calibrate(options, optimisation_method, end_criteria)

            tolerance = 3.0e-3

            self.assertFalse(model.sigma > tolerance)

            self.assertAlmostEqual(
                model.kappa * model.theta,
                model.kappa * volatility ** 2,
                delta=tolerance
            )
            self.assertAlmostEqual(model.v0, volatility ** 2, delta=tolerance)
Ejemplo n.º 23
0
def heston_calibration(df_option, ival=None):
    """
    calibrate heston model
    """

    # extract rates and div yields from the data set    
    df_tmp = DataFrame.filter(df_option, items=['dtExpiry', 'iRate', 'iDiv'])
    grouped = df_tmp.groupby('dtExpiry')

    def aggregate(serie):
        return serie[serie.index[0]]

    df_rates = grouped.agg(aggregate)

    # Get first index:
    first_index = 0

    dtTrade = df_option['dtTrade'][first_index]
    # back out the spot from any forward
    iRate = df_option['iRate'][first_index]
    iDiv = df_option['iDiv'][first_index]
    TTM = df_option['TTM'][first_index]
    Fwd = df_option['Fwd'][first_index]
    spot = SimpleQuote(Fwd*np.exp(-(iRate-iDiv)*TTM))
    print('Spot: %f risk-free rate: %f div. yield: %f' % (spot.value, iRate, iDiv))

    # build array of option helpers
    hh = heston_helpers(spot, df_option, dtTrade, df_rates)
    options = hh['options']
    spot = hh['spot']

    risk_free_ts = dfToZeroCurve(df_rates['iRate'], dtTrade)
    dividend_ts = dfToZeroCurve(df_rates['iDiv'], dtTrade)

    # initial values for parameters
    if ival is None:
        ival = {'v0': 0.1, 'kappa': 1.0, 'theta': 0.1,
        'sigma': 0.5, 'rho': -.5}

    process = HestonProcess(
        risk_free_ts, dividend_ts, spot, ival['v0'], ival['kappa'],
         ival['theta'], ival['sigma'], ival['rho'])

    model = HestonModel(process)
    engine = AnalyticHestonEngine(model, 64)

    for option in options:
        option.set_pricing_engine(engine)

    om = LevenbergMarquardt(1e-8, 1e-8, 1e-8)
    model.calibrate(
        options, om, EndCriteria(400, 40, 1.0e-8, 1.0e-8, 1.0e-8)
    )

    print('model calibration results:')
    print('v0: %f kappa: %f theta: %f sigma: %f rho: %f' %
          (model.v0, model.kappa, model.theta, model.sigma,
           model.rho))

    calib_error = (1.0/len(options)) * sum(
        [pow(o.calibration_error()*100.0,2) for o in options])

    print('SSE: %f' % calib_error)

    # merge the fitted volatility and the input data set
    return merge_df(df_option, options, 'Heston')