def test_BondEmbeddedOptionQUANTLIB():

    # Based on example at the nice blog on Quantlib at
    # http://gouthamanbalaraman.com/blog/callable-bond-quantlib-python.html
    # I get a price of 68.97 for 1000 time steps which is higher than the
    # 68.38 found in blog article. But this is for 40 grid points.
    # Note also that a basis point vol of 0.120 is 12% which is VERY HIGH!

    valuation_date = Date(16, 8, 2016)
    settlement_date = valuation_date.addWeekDays(3)

    ###########################################################################

    discount_curve = DiscountCurveFlat(valuation_date, 0.035,
                                       FrequencyTypes.SEMI_ANNUAL)

    ###########################################################################

    issue_date = Date(15, 9, 2010)
    maturity_date = Date(15, 9, 2022)
    coupon = 0.025
    freq_type = FrequencyTypes.QUARTERLY
    accrual_type = DayCountTypes.ACT_ACT_ICMA
    bond = Bond(issue_date, maturity_date, coupon, freq_type, accrual_type)

    ###########################################################################
    # Set up the call and put times and prices
    ###########################################################################

    nextCallDate = Date(15, 9, 2016)
    call_dates = [nextCallDate]
    call_prices = [100.0]

    for _ in range(1, 24):
        nextCallDate = nextCallDate.addMonths(3)
        call_dates.append(nextCallDate)
        call_prices.append(100.0)

    put_dates = []
    put_prices = []

    # the value used in blog of 12% bp vol is unrealistic
    sigma = 0.12  # basis point volatility
    a = 0.03

    puttableBond = BondEmbeddedOption(issue_date,
                                      maturity_date, coupon,
                                      freq_type, accrual_type,
                                      call_dates, call_prices,
                                      put_dates, put_prices)

    testCases.header("BOND PRICE", "PRICE")
    v = bond.clean_price_from_discount_curve(settlement_date, discount_curve)
    testCases.print("Bond Pure Price:", v)

    testCases.header("TIME", "NumTimeSteps", "BondWithOption", "BondPure")
    timeSteps = range(100, 1000, 100)
    values = []
    for numTimeSteps in timeSteps:
        model = FinModelRatesHW(sigma, a, numTimeSteps)
        start = time.time()
        v = puttableBond.value(settlement_date, discount_curve, model)
        end = time.time()
        period = end - start
        testCases.print(period, numTimeSteps, v['bondwithoption'], v['bondpure'])
        values.append(v['bondwithoption'])

    if plotGraphs:
        plt.figure()
        plt.title("Puttable Bond Price Convergence")
        plt.plot(timeSteps, values)
Exemplo n.º 2
0
def test_BDTExampleThree():
    # Valuation of a swaption as in Leif Andersen's paper - see Table 1 on
    # SSRN-id155208.pdf

    settlement_date = Date(1, 1, 2020)
    times = np.array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0])
    dates = settlement_date.add_years(times)
    rate = 0.06
    dfs = 1.0 / (1.0 + rate / 2.0)**(2.0 * times)
    curve = DiscountCurve(settlement_date, dates, dfs)

    coupon = 0.06
    freq_type = FrequencyTypes.SEMI_ANNUAL
    accrual_type = DayCountTypes.ACT_ACT_ICMA
    strike_price = 100.0
    face = 100.0
    # Andersen paper
    num_time_steps = 200

    exercise_type = FinExerciseTypes.EUROPEAN
    years_to_maturity = 4.0
    expiryYears = 2.0

    maturity_date = settlement_date.add_years(years_to_maturity)
    issue_date = Date(maturity_date._d, maturity_date._m, 2000)

    sigma = 0.2012

    expiry_date = settlement_date.add_years(expiryYears)

    tmat = (maturity_date - settlement_date) / gDaysInYear
    texp = (expiry_date - settlement_date) / gDaysInYear

    bond = Bond(issue_date, maturity_date, coupon, freq_type, accrual_type)

    coupon_times = []
    coupon_flows = []
    cpn = bond._coupon / bond._frequency
    for flow_date in bond._coupon_dates:
        if flow_date > expiry_date:
            flow_time = (flow_date - settlement_date) / gDaysInYear
            coupon_times.append(flow_time)
            coupon_flows.append(cpn)

    coupon_times = np.array(coupon_times)
    coupon_flows = np.array(coupon_flows)

    price = bond.clean_price_from_discount_curve(settlement_date, curve)

    model = BDTTree(sigma, num_time_steps)
    model.build_tree(tmat, times, dfs)

    v = model.bermudan_swaption(texp, tmat, strike_price, face, coupon_times,
                                coupon_flows, exercise_type)

    assert round(price, 5) == 100.01832
    assert round(v['pay'] * 100, 2) == 0.00
    assert round(v['rec'] * 100, 2) == 8883.21

    exercise_type = FinExerciseTypes.BERMUDAN
    years_to_maturity = 10.0
    expiryYears = 5.0

    maturity_date = settlement_date.add_years(years_to_maturity)
    issue_date = Date(maturity_date._d, maturity_date._m, 2000)

    sigma = 0.1522

    expiry_date = settlement_date.add_years(expiryYears)

    tmat = (maturity_date - settlement_date) / gDaysInYear
    texp = (expiry_date - settlement_date) / gDaysInYear

    bond = Bond(issue_date, maturity_date, coupon, freq_type, accrual_type)

    coupon_times = []
    coupon_flows = []
    cpn = bond._coupon / bond._frequency
    for flow_date in bond._coupon_dates:
        if flow_date > expiry_date:
            flow_time = (flow_date - settlement_date) / gDaysInYear
            coupon_times.append(flow_time)
            coupon_flows.append(cpn)

    coupon_times = np.array(coupon_times)
    coupon_flows = np.array(coupon_flows)

    price = bond.clean_price_from_discount_curve(settlement_date, curve)

    model = BDTTree(sigma, num_time_steps)
    model.build_tree(tmat, times, dfs)

    v = model.bermudan_swaption(texp, tmat, strike_price, face, coupon_times,
                                coupon_flows, exercise_type)

    assert round(price, 5) == 100.08625
    assert round(v['pay'] * 100, 2) == 263.28
    assert round(v['rec'] * 100, 2) == 7437.00
def test_BondOptionZEROVOLConvergence():

    # Build discount curve
    settlement_date = Date(1, 9, 2019)
    rate = 0.05
    discount_curve = DiscountCurveFlat(settlement_date, rate,
                                       FrequencyTypes.ANNUAL)

    # Bond details
    issue_date = Date(1, 9, 2014)
    maturity_date = Date(1, 9, 2025)
    coupon = 0.06
    freq_type = FrequencyTypes.ANNUAL
    accrual_type = DayCountTypes.ACT_ACT_ICMA
    bond = Bond(issue_date, maturity_date, coupon, freq_type, accrual_type)

    # Option Details
    expiry_date = Date(1, 12, 2021)
    face = 100.0

    dfExpiry = discount_curve.df(expiry_date)
    fwdCleanValue = bond.clean_price_from_discount_curve(
        expiry_date, discount_curve)
    fwdFullValue = bond.full_price_from_discount_curve(expiry_date,
                                                       discount_curve)
    #    print("BOND FwdCleanBondPx", fwdCleanValue)
    #    print("BOND FwdFullBondPx", fwdFullValue)
    #    print("BOND Accrued:", bond._accrued_interest)

    spotCleanValue = bond.clean_price_from_discount_curve(
        settlement_date, discount_curve)

    testCases.header("STRIKE", "STEPS", "CALL_INT", "CALL_INT_PV", "CALL_EUR",
                     "CALL_AMER", "PUT_INT", "PUT_INT_PV", "PUT_EUR",
                     "PUT_AMER")

    num_time_steps = range(100, 200, 100)
    strike_prices = [90, 100, 110]

    for strike_price in strike_prices:

        callIntrinsic = max(spotCleanValue - strike_price, 0)
        putIntrinsic = max(strike_price - spotCleanValue, 0)
        callIntrinsicPV = max(fwdCleanValue - strike_price, 0) * dfExpiry
        putIntrinsicPV = max(strike_price - fwdCleanValue, 0) * dfExpiry

        for num_steps in num_time_steps:

            sigma = 0.0000001
            a = 0.1
            model = BKTree(sigma, a, num_steps)

            option_type = OptionTypes.EUROPEAN_CALL
            bond_option1 = BondOption(bond, expiry_date, strike_price, face,
                                      option_type)
            v1 = bond_option1.value(settlement_date, discount_curve, model)

            option_type = OptionTypes.AMERICAN_CALL
            bond_option2 = BondOption(bond, expiry_date, strike_price, face,
                                      option_type)
            v2 = bond_option2.value(settlement_date, discount_curve, model)

            option_type = OptionTypes.EUROPEAN_PUT
            bond_option3 = BondOption(bond, expiry_date, strike_price, face,
                                      option_type)
            v3 = bond_option3.value(settlement_date, discount_curve, model)

            option_type = OptionTypes.AMERICAN_PUT
            bond_option4 = BondOption(bond, expiry_date, strike_price, face,
                                      option_type)
            v4 = bond_option4.value(settlement_date, discount_curve, model)

            testCases.print(strike_price, num_steps, callIntrinsic,
                            callIntrinsicPV, v1, v2, putIntrinsic,
                            putIntrinsicPV, v3, v4)
Exemplo n.º 4
0
def test_BDTExampleTwo():
    # Valuation of a European option on a coupon bearing bond
    # This follows example in Fig 28.11 of John Hull's book (6th Edition)
    # but does not have the exact same dt so there are some differences

    settlement_date = Date(1, 12, 2019)
    issue_date = Date(1, 12, 2015)
    expiry_date = settlement_date.add_tenor("18m")
    maturity_date = settlement_date.add_tenor("10Y")
    coupon = 0.05
    freq_type = FrequencyTypes.SEMI_ANNUAL
    accrual_type = DayCountTypes.ACT_ACT_ICMA
    bond = Bond(issue_date, maturity_date, coupon, freq_type, accrual_type)

    coupon_times = []
    coupon_flows = []
    cpn = bond._coupon / bond._frequency
    num_flows = len(bond._coupon_dates)

    for i in range(1, num_flows):
        pcd = bond._coupon_dates[i - 1]
        ncd = bond._coupon_dates[i]
        if pcd < settlement_date and ncd > settlement_date:
            flow_time = (pcd - settlement_date) / gDaysInYear
            coupon_times.append(flow_time)
            coupon_flows.append(cpn)

    for flow_date in bond._coupon_dates:
        if flow_date > settlement_date:
            flow_time = (flow_date - settlement_date) / gDaysInYear
            coupon_times.append(flow_time)
            coupon_flows.append(cpn)

    coupon_times = np.array(coupon_times)
    coupon_flows = np.array(coupon_flows)

    strike_price = 105.0
    face = 100.0

    tmat = (maturity_date - settlement_date) / gDaysInYear
    texp = (expiry_date - settlement_date) / gDaysInYear
    times = np.linspace(0, tmat, 11)
    dates = settlement_date.add_years(times)
    dfs = np.exp(-0.05 * times)

    curve = DiscountCurve(settlement_date, dates, dfs)

    price = bond.clean_price_from_discount_curve(settlement_date, curve)
    assert round(price, 4) == 99.5420

    sigma = 0.20

    # Test convergence
    num_time_steps = 5
    exercise_type = FinExerciseTypes.AMERICAN

    model = BDTTree(sigma, num_time_steps)
    model.build_tree(tmat, times, dfs)
    v = model.bond_option(texp, strike_price, face, coupon_times, coupon_flows,
                          exercise_type)

    assert round(v['call'], 4) == 0.5043
    assert round(v['put'], 4) == 8.2242
Exemplo n.º 5
0
def test_BondOptionAmericanConvergenceTWO():

    # Build discount curve
    settlement_date = Date(1, 12, 2019)
    discount_curve = DiscountCurveFlat(settlement_date, 0.05)

    # Bond details
    issue_date = Date(1, 12, 2015)
    maturity_date = settlement_date.add_tenor("10Y")
    coupon = 0.05
    freq_type = FrequencyTypes.SEMI_ANNUAL
    accrual_type = DayCountTypes.ACT_ACT_ICMA
    bond = Bond(issue_date, maturity_date, coupon, freq_type, accrual_type)
    expiry_date = settlement_date.add_tenor("18m")
    face = 100.0

    spotValue = bond.clean_price_from_discount_curve(settlement_date,
                                                     discount_curve)
    testCases.header("LABEL", "VALUE")
    testCases.print("BOND PRICE", spotValue)

    testCases.header("TIME", "N", "EUR_CALL", "AMER_CALL", "EUR_PUT",
                     "AMER_PUT")

    sigma = 0.01
    a = 0.1
    hwModel = HWTree(sigma, a)
    K = 102.0

    vec_ec = []
    vec_ac = []
    vec_ep = []
    vec_ap = []

    num_stepsVector = range(100, 500, 100)

    for num_steps in num_stepsVector:
        hwModel = HWTree(sigma, a, num_steps)

        start = time.time()

        europeanCallBondOption = BondOption(bond, expiry_date, K, face,
                                            FinOptionTypes.EUROPEAN_CALL)

        v_ec = europeanCallBondOption.value(settlement_date, discount_curve,
                                            hwModel)

        americanCallBondOption = BondOption(bond, expiry_date, K, face,
                                            FinOptionTypes.AMERICAN_CALL)

        v_ac = americanCallBondOption.value(settlement_date, discount_curve,
                                            hwModel)

        europeanPutBondOption = BondOption(bond, expiry_date, K, face,
                                           FinOptionTypes.EUROPEAN_PUT)

        v_ep = europeanPutBondOption.value(settlement_date, discount_curve,
                                           hwModel)

        americanPutBondOption = BondOption(bond, expiry_date, K, face,
                                           FinOptionTypes.AMERICAN_PUT)

        v_ap = americanPutBondOption.value(settlement_date, discount_curve,
                                           hwModel)

        end = time.time()
        period = end - start

        testCases.print(period, num_steps, v_ec, v_ac, v_ep, v_ap)

        vec_ec.append(v_ec)
        vec_ac.append(v_ac)
        vec_ep.append(v_ep)
        vec_ap.append(v_ap)

    if plotGraphs:
        plt.figure()
        plt.plot(num_stepsVector, vec_ac, label="American Call")
        plt.legend()

        plt.figure()
        plt.plot(num_stepsVector, vec_ap, label="American Put")
        plt.legend()
Exemplo n.º 6
0
def test_BondOption():

    settlement_date = Date(1, 12, 2019)
    issue_date = Date(1, 12, 2018)
    maturity_date = settlement_date.add_tenor("10Y")
    coupon = 0.05
    freq_type = FrequencyTypes.SEMI_ANNUAL
    accrual_type = DayCountTypes.ACT_ACT_ICMA
    bond = Bond(issue_date, maturity_date, coupon, freq_type, accrual_type)

    times = np.linspace(0, 10.0, 21)
    dfs = np.exp(-0.05 * times)
    dates = settlement_date.add_years(times)
    discount_curve = DiscountCurve(settlement_date, dates, dfs)

    expiry_date = settlement_date.add_tenor("18m")
    strike_price = 105.0
    face = 100.0

    ###########################################################################

    strikes = [80, 85, 90, 95, 100, 105, 110, 115, 120]

    option_type = FinOptionTypes.EUROPEAN_CALL

    testCases.header("LABEL", "VALUE")

    price = bond.clean_price_from_discount_curve(settlement_date,
                                                 discount_curve)
    testCases.print("Fixed Income Price:", price)

    num_time_steps = 100

    testCases.banner("HW EUROPEAN CALL")
    testCases.header("STRIKE", "VALUE")

    for strike_price in strikes:

        sigma = 0.01
        a = 0.1

        bond_option = BondOption(bond, expiry_date, strike_price, face,
                                 option_type)

        model = HWTree(sigma, a, num_time_steps)
        v = bond_option.value(settlement_date, discount_curve, model)
        testCases.print(strike_price, v)

    ###########################################################################

    option_type = FinOptionTypes.AMERICAN_CALL

    price = bond.clean_price_from_discount_curve(settlement_date,
                                                 discount_curve)
    testCases.header("LABEL", "VALUE")
    testCases.print("Fixed Income Price:", price)

    testCases.banner("HW AMERICAN CALL")
    testCases.header("STRIKE", "VALUE")

    for strike_price in strikes:

        sigma = 0.01
        a = 0.1

        bond_option = BondOption(bond, expiry_date, strike_price, face,
                                 option_type)

        model = HWTree(sigma, a)
        v = bond_option.value(settlement_date, discount_curve, model)
        testCases.print(strike_price, v)

    ###########################################################################

    option_type = FinOptionTypes.EUROPEAN_PUT
    testCases.banner("HW EUROPEAN PUT")
    testCases.header("STRIKE", "VALUE")

    price = bond.clean_price_from_discount_curve(settlement_date,
                                                 discount_curve)

    for strike_price in strikes:

        sigma = 0.01
        a = 0.1

        bond_option = BondOption(bond, expiry_date, strike_price, face,
                                 option_type)

        model = HWTree(sigma, a)
        v = bond_option.value(settlement_date, discount_curve, model)
        testCases.print(strike_price, v)

    ###########################################################################

    option_type = FinOptionTypes.AMERICAN_PUT
    testCases.banner("HW AMERICAN PUT")
    testCases.header("STRIKE", "VALUE")

    price = bond.clean_price_from_discount_curve(settlement_date,
                                                 discount_curve)

    for strike_price in strikes:

        sigma = 0.02
        a = 0.1

        bond_option = BondOption(bond, expiry_date, strike_price, face,
                                 option_type)

        model = HWTree(sigma, a)
        v = bond_option.value(settlement_date, discount_curve, model)
        testCases.print(strike_price, v)
Exemplo n.º 7
0
def test_HullWhiteCallableBond():
    # Valuation of a European option on a coupon bearing bond

    settlement_date = Date(1, 12, 2019)
    issue_date = Date(1, 12, 2018)
    maturity_date = settlement_date.add_tenor("10Y")
    coupon = 0.05
    freq_type = FrequencyTypes.SEMI_ANNUAL
    accrual_type = DayCountTypes.ACT_ACT_ICMA
    bond = Bond(issue_date, maturity_date, coupon, freq_type, accrual_type)

    coupon_times = []
    coupon_flows = []
    cpn = bond._coupon / bond._frequency

    for flow_date in bond._flow_dates[1:]:

        if flow_date > settlement_date:
            flow_time = (flow_date - settlement_date) / gDaysInYear
            coupon_times.append(flow_time)
            coupon_flows.append(cpn)

    coupon_times = np.array(coupon_times)
    coupon_flows = np.array(coupon_flows)

    ###########################################################################
    # Set up the call and put times and prices
    ###########################################################################

    call_dates = []
    call_prices = []
    callPx = 120.0
    call_dates.append(settlement_date.add_tenor("2Y"))
    call_prices.append(callPx)
    call_dates.append(settlement_date.add_tenor("3Y"))
    call_prices.append(callPx)
    call_dates.append(settlement_date.add_tenor("4Y"))
    call_prices.append(callPx)
    call_dates.append(settlement_date.add_tenor("5Y"))
    call_prices.append(callPx)
    call_dates.append(settlement_date.add_tenor("6Y"))
    call_prices.append(callPx)
    call_dates.append(settlement_date.add_tenor("7Y"))
    call_prices.append(callPx)
    call_dates.append(settlement_date.add_tenor("8Y"))
    call_prices.append(callPx)

    call_times = []
    for dt in call_dates:
        t = (dt - settlement_date) / gDaysInYear
        call_times.append(t)

    put_dates = []
    put_prices = []
    putPx = 98.0
    put_dates.append(settlement_date.add_tenor("2Y"))
    put_prices.append(putPx)
    put_dates.append(settlement_date.add_tenor("3Y"))
    put_prices.append(putPx)
    put_dates.append(settlement_date.add_tenor("4Y"))
    put_prices.append(putPx)
    put_dates.append(settlement_date.add_tenor("5Y"))
    put_prices.append(putPx)
    put_dates.append(settlement_date.add_tenor("6Y"))
    put_prices.append(putPx)
    put_dates.append(settlement_date.add_tenor("7Y"))
    put_prices.append(putPx)
    put_dates.append(settlement_date.add_tenor("8Y"))
    put_prices.append(putPx)

    put_times = []
    for dt in put_dates:
        t = (dt - settlement_date) / gDaysInYear
        put_times.append(t)

    ###########################################################################

    tmat = (maturity_date - settlement_date) / gDaysInYear
    curve = DiscountCurveFlat(settlement_date, 0.05, FrequencyTypes.CONTINUOUS)

    dfs = []
    times = []

    for dt in bond._flow_dates:
        if dt > settlement_date:
            t = (dt - settlement_date) / gDaysInYear
            df = curve.df(dt)
            times.append(t)
            dfs.append(df)

    dfs = np.array(dfs)
    times = np.array(times)

    ###########################################################################

    v1 = bond.clean_price_from_discount_curve(settlement_date, curve)

    sigma = 0.02  # basis point volatility
    a = 0.01

    # Test convergence
    num_steps_list = [100, 200, 500, 1000]
    tmat = (maturity_date - settlement_date) / gDaysInYear

    testCases.header("NUMSTEPS", "TIME", "BOND_ONLY", "CALLABLE_BOND")

    for num_time_steps in num_steps_list:

        start = time.time()
        model = HWTree(sigma, a, num_time_steps)
        model.build_tree(tmat, times, dfs)

        v2 = model.callable_puttable_bond_tree(coupon_times, coupon_flows,
                                               call_times, call_prices,
                                               put_times, put_prices, 100.0)

        end = time.time()
        period = end - start
        testCases.print(num_time_steps, period, v1, v2)
Exemplo n.º 8
0
def test_BondOptionDerivaGem():

    # See https://github.com/domokane/FinancePy/issues/98

    settlement_date = Date(1, 12, 2019)

    rate = 0.05
    dcType = DayCountTypes.THIRTY_360_BOND
    fixedFreq = FrequencyTypes.SEMI_ANNUAL
    discount_curve = DiscountCurveFlat(settlement_date, rate, fixedFreq,
                                       dcType)

    issue_date = Date(1, 12, 2018)
    expiry_date = settlement_date.add_tenor("18m")
    maturity_date = settlement_date.add_tenor("10Y")

    coupon = 0.05
    freqType = FrequencyTypes.SEMI_ANNUAL
    accrualType = DayCountTypes.THIRTY_360_BOND
    bond = Bond(issue_date, maturity_date, coupon, freqType, accrualType)
    strike_price = 100.0
    face = 100.0

    europeanCallBondOption = BondOption(bond, expiry_date, strike_price, face,
                                        OptionTypes.EUROPEAN_CALL)
    cp = bond.clean_price_from_discount_curve(expiry_date, discount_curve)
    fp = bond.full_price_from_discount_curve(expiry_date, discount_curve)
    #    print("Fixed Income Clean Price: %9.3f"% cp)
    #    print("Fixed Income Full  Price: %9.3f"% fp)

    num_steps = 500
    sigma = 0.0125
    a = 0.1
    modelHW = HWTree(sigma, a, num_steps)

    ec = europeanCallBondOption.value(settlement_date, discount_curve, modelHW)

    ###########################################################################

    couponTimes = []
    couponFlows = []
    cpn = bond._coupon / bond._frequency

    numFlows = len(bond._coupon_dates)
    for i in range(0, numFlows):

        pcd = bond._coupon_dates[i - 1]
        ncd = bond._coupon_dates[i]

        if ncd > settlement_date:

            if len(couponTimes) == 0:
                flowTime = (pcd - settlement_date) / gDaysInYear
                couponTimes.append(flowTime)
                couponFlows.append(cpn)

            flowTime = (ncd - settlement_date) / gDaysInYear
            couponTimes.append(flowTime)
            couponFlows.append(cpn)

    couponTimes = np.array(couponTimes)
    couponFlows = np.array(couponFlows)

    y = 0.05
    times = np.linspace(0, 10, 21)
    dfs = np.power(1 + y / 2, -times * 2)

    sigma = 0.0125
    a = 0.1
    model = HWTree(sigma, a, None)

    #  Test convergence
    texp = (expiry_date - settlement_date) / gDaysInYear
    tmat = (maturity_date - settlement_date) / gDaysInYear

    # Jamshidian approach
    vjam = model.european_bond_option_jamshidian(texp, strike_price, face,
                                                 couponTimes, couponFlows,
                                                 times, dfs)
    # print("Jamshidian:", vjam)

    model._num_time_steps = 100
    model.build_tree(tmat, times, dfs)
    exerciseType = FinExerciseTypes.EUROPEAN

    vHW = model.bond_option(texp, strike_price, face, couponTimes, couponFlows,
                            exerciseType)