def test_5(): accrual_type = DayCountTypes.THIRTY_E_PLUS_360 maturityDt = Date(7, 9, 2014) coupon = 0.0500000000 clean_price = 109.35500000 issueDt = Date(maturityDt._d, maturityDt._m, 2000) freq_type = FrequencyTypes.SEMI_ANNUAL bond = Bond(issueDt, maturityDt, coupon, freq_type, accrual_type) ytm = bond.yield_to_maturity(settlement, clean_price) assert round(bond._accrued_interest, 4) == 0.1667 assert round(ytm * 100, 4) == 0.2297
def test_6(): accrual_type = DayCountTypes.ACT_ACT_ISDA maturityDt = Date(22, 1, 2015) coupon = 0.0275000000 clean_price = 105.62500000 issueDt = Date(maturityDt._d, maturityDt._m, 2000) freq_type = FrequencyTypes.SEMI_ANNUAL bond = Bond(issueDt, maturityDt, coupon, freq_type, accrual_type) ytm = bond.yield_to_maturity(settlement, clean_price) assert round(bond._accrued_interest, 4) == 0.4433 assert round(ytm * 100, 4) == 0.3334
def test_3(): accrual_type = DayCountTypes.THIRTY_E_360 maturityDt = Date(27, 9, 2013) coupon = 0.080000 clean_price = 107.92000000 issueDt = Date(maturityDt._d, maturityDt._m, 2000) freq_type = FrequencyTypes.SEMI_ANNUAL bond = Bond(issueDt, maturityDt, coupon, freq_type, accrual_type) ytm = bond.yield_to_maturity(settlement, clean_price) assert round(bond._accrued_interest, 4) == 3.8222 assert round(ytm * 100, 4) == 0.2380
def test_4(): accrual_type = DayCountTypes.THIRTY_E_360_ISDA maturityDt = Date(7, 3, 2014) coupon = 0.022500 clean_price = 102.9750 issueDt = Date(maturityDt._d, maturityDt._m, 2000) freq_type = FrequencyTypes.SEMI_ANNUAL bond = Bond(issueDt, maturityDt, coupon, freq_type, accrual_type) ytm = bond.yield_to_maturity(settlement, clean_price) assert round(bond._accrued_interest, 4) == 0.0750 assert round(ytm * 100, 4) == 0.2172
def test_11(): accrual_type = DayCountTypes.SIMPLE maturityDt = Date(25, 8, 2017) coupon = 0.0875000000 clean_price = 138.57000000 issueDt = Date(maturityDt._d, maturityDt._m, 2000) freq_type = FrequencyTypes.SEMI_ANNUAL bond = Bond(issueDt, maturityDt, coupon, freq_type, accrual_type) ytm = bond.yield_to_maturity(settlement, clean_price) assert round(bond._accrued_interest, 4) == 0.5993 assert round(ytm * 100, 4) == 0.7652
def test_2(): accrual_type = DayCountTypes.THIRTY_360_BOND maturityDt = Date(7, 3, 2013) coupon = 0.045 clean_price = 101.99500000 issueDt = Date(maturityDt._d, maturityDt._m, 2000) freq_type = FrequencyTypes.SEMI_ANNUAL bond = Bond(issueDt, maturityDt, coupon, freq_type, accrual_type) ytm = bond.yield_to_maturity(settlement, clean_price) assert round(bond._accrued_interest, 4) == 0.1500 assert round(ytm * 100, 4) == 0.2203
def test_9(): accrual_type = DayCountTypes.ACT_360 maturityDt = Date(22, 1, 2016) coupon = 0.0200000000 clean_price = 104.98000000 issueDt = Date(maturityDt._d, maturityDt._m, 2000) freq_type = FrequencyTypes.SEMI_ANNUAL bond = Bond(issueDt, maturityDt, coupon, freq_type, accrual_type) ytm = bond.yield_to_maturity(settlement, clean_price) assert round(bond._accrued_interest, 4) == 0.3278 assert round(ytm * 100, 4) == 0.4930
def test_10(): accrual_type = DayCountTypes.ACT_365L maturityDt = Date(7, 9, 2016) coupon = 0.0400000000 clean_price = 113.49500000 issueDt = Date(maturityDt._d, maturityDt._m, 2000) freq_type = FrequencyTypes.SEMI_ANNUAL bond = Bond(issueDt, maturityDt, coupon, freq_type, accrual_type) ytm = bond.yield_to_maturity(settlement, clean_price) assert round(bond._accrued_interest, 4) == 0.1315 assert round(ytm * 100, 4) == 0.5559
def test_8(): accrual_type = DayCountTypes.ACT_365F maturityDt = Date(7, 12, 2015) coupon = 0.0800000000 clean_price = 124.47000000 issueDt = Date(maturityDt._d, maturityDt._m, 2000) freq_type = FrequencyTypes.SEMI_ANNUAL bond = Bond(issueDt, maturityDt, coupon, freq_type, accrual_type) ytm = bond.yield_to_maturity(settlement, clean_price) assert round(bond._accrued_interest, 4) == 2.2795 assert round(ytm * 100, 4) == 0.3405
def test_7(): accrual_type = DayCountTypes.ACT_ACT_ICMA maturityDt = Date(7, 9, 2015) coupon = 0.0475000000 clean_price = 112.98000000 issueDt = Date(maturityDt._d, maturityDt._m, 2000) freq_type = FrequencyTypes.SEMI_ANNUAL bond = Bond(issueDt, maturityDt, coupon, freq_type, accrual_type) ytm = bond.yield_to_maturity(settlement, clean_price) assert round(bond._accrued_interest, 4) == 0.1575 assert round(ytm * 100, 4) == 0.3485
def test_BondOptionAmericanConvergenceONE(): # Build discount curve settlement_date = Date(1, 12, 2019) discount_curve = DiscountCurveFlat(settlement_date, 0.05) # Bond details maturity_date = Date(1, 9, 2025) issue_date = Date(1, 9, 2016) 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) # Option Details expiry_date = Date(1, 12, 2020) strike_price = 100.0 face = 100.0 testCases.header("TIME", "N", "PUT_AMER", "PUT_EUR", "CALL_AME", "CALL_EUR") timeSteps = range(30, 100, 10) for num_time_steps in timeSteps: sigma = 0.20 a = 0.1 start = time.time() option_type = OptionTypes.AMERICAN_PUT bond_option1 = BondOption( bond, expiry_date, strike_price, face, option_type) model1 = BKTree(sigma, a, num_time_steps) v1put = bond_option1.value(settlement_date, discount_curve, model1) option_type = OptionTypes.EUROPEAN_PUT bond_option2 = BondOption( bond, expiry_date, strike_price, face, option_type) model2 = BKTree(sigma, a, num_time_steps) v2put = bond_option2.value(settlement_date, discount_curve, model2) option_type = OptionTypes.AMERICAN_CALL bond_option1 = BondOption( bond, expiry_date, strike_price, face, option_type) model1 = BKTree(sigma, a, num_time_steps) v1call = bond_option1.value(settlement_date, discount_curve, model1) option_type = OptionTypes.EUROPEAN_CALL bond_option2 = BondOption( bond, expiry_date, strike_price, face, option_type) model2 = BKTree(sigma, a, num_time_steps) v2call = bond_option2.value(settlement_date, discount_curve, model2) end = time.time() period = end - start testCases.print(period, num_time_steps, v1put, v2put, v1call, v2call)
def test_bond_future_2(): bond = Bond(issue_date, Date(15, 8, 2027), 0.0225, freq, basis) assert bond._maturity_date == Date(15, 8, 2027) settlement_date = Date(10, 10, 2017) price = 99 + 1 / 32 yld = bond.yield_to_maturity(settlement_date, price) assert round(yld, 4) == 0.0236 first_delivery_date = Date(1, 12, 2017) last_delivery_date = Date(28, 12, 2017) contract_size = 100000 contractCoupon = 0.06 bondFutureContract = BondFuture("TYZ7", first_delivery_date, last_delivery_date, contract_size, contractCoupon) cf = bondFutureContract.conversion_factor(bond) assert round(cf, 4) == 74.2122 futures_price = 125.265625 pip = bondFutureContract.principal_invoice_price(bond, futures_price) assert round(pip, 4) == 9296237.6200 tia = bondFutureContract.total_invoice_amount( settlement_date, bond, futures_price) assert round(tia, 4) == 9296580.0100
def test_bond_future_1(): bond = Bond(issue_date, Date(15, 8, 2011), 0.0500, freq, basis) assert bond._maturity_date == Date(15, 8, 2011) assert bond._coupon * 100 == 5.0 first_delivery_date = Date(1, 3, 2002) last_delivery_date = Date(28, 3, 2002) contract_size = 100000 contractCoupon = 0.06 bondFutureContract = BondFuture("TYH2", first_delivery_date, last_delivery_date, contract_size, contractCoupon) cf = bondFutureContract.conversion_factor(bond) assert round(cf, 4) == 92.9688
def test_BondZeroCurve(): import pandas as pd path = os.path.join(os.path.dirname(__file__), './data/giltBondPrices.txt') bondDataFrame = pd.read_csv(path, sep='\t') bondDataFrame['mid'] = 0.5 * (bondDataFrame['bid'] + bondDataFrame['ask']) freq_type = FrequencyTypes.SEMI_ANNUAL accrual_type = DayCountTypes.ACT_ACT_ICMA settlement = Date(19, 9, 2012) bonds = [] clean_prices = [] for _, bondRow in bondDataFrame.iterrows(): dateString = bondRow['maturity'] matDatetime = dt.datetime.strptime(dateString, '%d-%b-%y') maturityDt = fromDatetime(matDatetime) issueDt = Date(maturityDt._d, maturityDt._m, 2000) coupon = bondRow['coupon'] / 100.0 clean_price = bondRow['mid'] bond = Bond(issueDt, maturityDt, coupon, freq_type, accrual_type) bonds.append(bond) clean_prices.append(clean_price) ############################################################################### bondCurve = BondZeroCurve(settlement, bonds, clean_prices) testCases.header("DATE", "ZERO RATE") for _, bond in bondDataFrame.iterrows(): dateString = bond['maturity'] matDatetime = dt.datetime.strptime(dateString, '%d-%b-%y') maturityDt = fromDatetime(matDatetime) zeroRate = bondCurve.zeroRate(maturityDt) testCases.print(maturityDt, zeroRate) if plotGraphs: bondCurve.plot("BOND CURVE")
def test_BondExDividend(): issue_date = Date(7, 9, 2000) maturity_date = Date(7, 9, 2020) coupon = 0.05 freq_type = FrequencyTypes.SEMI_ANNUAL accrual_type = DayCountTypes.ACT_ACT_ICMA face = 100.0 exDivDays = 7 testCases.header("LABEL", "VALUE") calendar_type = CalendarTypes.UNITED_KINGDOM bond = Bond(issue_date, maturity_date, coupon, freq_type, accrual_type, face) settlement_date = Date(7, 9, 2003) accrued = bond.calc_accrued_interest(settlement_date, exDivDays, calendar_type) testCases.print("SettlementDate:", settlement_date) testCases.print("Accrued:", accrued) ########################################################################### testCases.banner("=======================================================") testCases.header("SETTLEMENT", "ACCRUED") issue_date = Date(7, 9, 2000) maturity_date = Date(7, 9, 2020) coupon = 0.05 freq_type = FrequencyTypes.SEMI_ANNUAL accrual_type = DayCountTypes.ACT_ACT_ICMA face = 100.0 exDivDays = 7 calendar_type = CalendarTypes.UNITED_KINGDOM bond = Bond(issue_date, maturity_date, coupon, freq_type, accrual_type, face) settlement_date = Date(25, 8, 2010) for _ in range(0, 13): settlement_date = settlement_date.add_days(1) accrued = bond.calc_accrued_interest(settlement_date, exDivDays, calendar_type) testCases.print(settlement_date, accrued)
def test_BKExampleTwo(): # Valuation of a European option on a coupon bearing bond # This follows example in Fig 28.11 of John Hull's book but does not # have the exact same dt so there are some differences settlement_date = Date(1, 12, 2019) issue_date = Date(1, 12, 2018) 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._flow_dates) for i in range(1, num_flows): pcd = bond._flow_dates[i - 1] ncd = bond._flow_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._flow_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) testCases.header("LABEL", "VALUE") testCases.print("Fixed Income Price:", price) sigma = 0.20 a = 0.05 num_time_steps = 26 model = BKTree(sigma, a, num_time_steps) model.build_tree(tmat, times, dfs) exercise_type = FinExerciseTypes.AMERICAN v = model.bond_option(texp, strike_price, face, coupon_times, coupon_flows, exercise_type) # Test convergence num_steps_list = [100, 200, 300, 500, 1000] exercise_type = FinExerciseTypes.AMERICAN testCases.header("TIMESTEPS", "TIME", "VALUE") treeVector = [] for num_time_steps in num_steps_list: start = time.time() model = BKTree(sigma, a, num_time_steps) model.build_tree(tmat, times, dfs) v = model.bond_option(texp, strike_price, face, coupon_times, coupon_flows, exercise_type) end = time.time() period = end - start treeVector.append(v) testCases.print(num_time_steps, period, v) # plt.plot(num_steps_list, treeVector) # Value in Hill converges to 0.699 with 100 time steps while I get 0.700 if 1 == 0: print("RT") print_tree(model._rt, 5) print("Q") print_tree(model._Q, 5)
def test_Bond(): import pandas as pd path = os.path.join(os.path.dirname(__file__), './data/giltBondPrices.txt') bondDataFrame = pd.read_csv(path, sep='\t') bondDataFrame['mid'] = 0.5 * (bondDataFrame['bid'] + bondDataFrame['ask']) freq_type = FrequencyTypes.SEMI_ANNUAL settlement_date = Date(19, 9, 2012) face = ONE_MILLION for accrual_type in DayCountTypes: testCases.header("MATURITY", "COUPON", "CLEAN_PRICE", "ACCD_DAYS", "ACCRUED", "YTM") for _, bond in bondDataFrame.iterrows(): dateString = bond['maturity'] matDatetime = dt.datetime.strptime(dateString, '%d-%b-%y') maturityDt = fromDatetime(matDatetime) issueDt = Date(maturityDt._d, maturityDt._m, 2000) coupon = bond['coupon'] / 100.0 clean_price = bond['mid'] bond = Bond(issueDt, maturityDt, coupon, freq_type, accrual_type, 100) ytm = bond.yield_to_maturity(settlement_date, clean_price) accrued_interest= bond._accruedInterest accd_days = bond._accrued_days testCases.print("%18s" % maturityDt, "%8.4f" % coupon, "%10.4f" % clean_price, "%6.0f" % accd_days, "%10.4f" % accrued_interest, "%8.4f" % ytm) ########################################################################### # EXAMPLE FROM http://bondtutor.com/btchp4/topic6/topic6.htm accrualConvention = DayCountTypes.ACT_ACT_ICMA y = 0.062267 settlement_date = Date(19, 4, 1994) issue_date = Date(15, 7, 1990) maturity_date = Date(15, 7, 1997) coupon = 0.085 face = ONE_MILLION freq_type = FrequencyTypes.SEMI_ANNUAL bond = Bond(issue_date, maturity_date, coupon, freq_type, accrualConvention, face) testCases.header("FIELD", "VALUE") full_price = bond.full_price_from_ytm(settlement_date, y) testCases.print("Full Price = ", full_price) clean_price = bond.clean_price_from_ytm(settlement_date, y) testCases.print("Clean Price = ", clean_price) accrued_interest= bond._accruedInterest testCases.print("Accrued = ", accrued_interest) ytm = bond.yield_to_maturity(settlement_date, clean_price) testCases.print("Yield to Maturity = ", ytm) bump = 1e-4 priceBumpedUp = bond.full_price_from_ytm(settlement_date, y + bump) testCases.print("Price Bumped Up:", priceBumpedUp) priceBumpedDn = bond.full_price_from_ytm(settlement_date, y - bump) testCases.print("Price Bumped Dn:", priceBumpedDn) durationByBump = -(priceBumpedUp - full_price) / bump testCases.print("Duration by Bump = ", durationByBump) duration = bond.dollar_duration(settlement_date, y) testCases.print("Dollar Duration = ", duration) testCases.print("Duration Difference:", duration - durationByBump) modified_duration = bond.modified_duration(settlement_date, y) testCases.print("Modified Duration = ", modified_duration) macauley_duration = bond.macauley_duration(settlement_date, y) testCases.print("Macauley Duration = ", macauley_duration) conv = bond.convexity_from_ytm(settlement_date, y) testCases.print("Convexity = ", conv) # ASSET SWAP SPREAD # When the libor curve is the flat bond curve then the ASW is zero by # definition flatCurve = DiscountCurveFlat(settlement_date, ytm, FrequencyTypes.SEMI_ANNUAL) testCases.header("FIELD", "VALUE") clean_price = bond.clean_price_from_ytm(settlement_date, ytm) asw = bond.asset_swap_spread(settlement_date, clean_price, flatCurve) testCases.print("Discounted on Bond Curve ASW:", asw * 10000) # When the libor curve is the Libor curve then the ASW is positive libor_curve = buildIborCurve(settlement_date) asw = bond.asset_swap_spread(settlement_date, clean_price, libor_curve) oas = bond.option_adjusted_spread(settlement_date, clean_price, libor_curve) testCases.print("Discounted on LIBOR Curve ASW:", asw * 10000) testCases.print("Discounted on LIBOR Curve OAS:", oas * 10000) p = 90.0 asw = bond.asset_swap_spread(settlement_date, p, libor_curve) oas = bond.option_adjusted_spread(settlement_date, p, libor_curve) testCases.print("Deep discount bond at 90 ASW:", asw * 10000) testCases.print("Deep discount bond at 90 OAS:", oas * 10000) p = 100.0 asw = bond.asset_swap_spread(settlement_date, p, libor_curve) oas = bond.option_adjusted_spread(settlement_date, p, libor_curve) testCases.print("Par bond at 100 ASW:", asw * 10000) testCases.print("Par bond at 100 OAS:", oas * 10000) p = 120.0 asw = bond.asset_swap_spread(settlement_date, p, libor_curve) oas = bond.option_adjusted_spread(settlement_date, p, libor_curve) testCases.print("Above par bond at 120 ASW:", asw * 10000) testCases.print("Above par bond at 120 OAS:", oas * 10000) ########################################################################## # https://data.bloomberglp.com/bat/sites/3/2017/07/SF-2017_Paul-Fjeldsted.pdf # Page 10 TREASURY NOTE SCREENSHOT ########################################################################## testCases.banner("BLOOMBERG US TREASURY EXAMPLE") settlement_date = Date(21, 7, 2017) issue_date = Date(15, 5, 2010) maturity_date = Date(15, 5, 2027) coupon = 0.02375 freq_type = FrequencyTypes.SEMI_ANNUAL accrual_type = DayCountTypes.ACT_ACT_ICMA face = 100.0 bond = Bond(issue_date, maturity_date, coupon, freq_type, accrual_type, face) testCases.header("FIELD", "VALUE") clean_price = 99.7808417 yld = bond.current_yield(clean_price) testCases.print("Current Yield = ", yld) ytm = bond.yield_to_maturity(settlement_date, clean_price, FinYTMCalcType.UK_DMO) testCases.print("UK DMO Yield To Maturity = ", ytm) ytm = bond.yield_to_maturity(settlement_date, clean_price, FinYTMCalcType.US_STREET) testCases.print("US STREET Yield To Maturity = ", ytm) ytm = bond.yield_to_maturity(settlement_date, clean_price, FinYTMCalcType.US_TREASURY) testCases.print("US TREASURY Yield To Maturity = ", ytm) full_price = bond.full_price_from_ytm(settlement_date, ytm) testCases.print("Full Price = ", full_price) clean_price = bond.clean_price_from_ytm(settlement_date, ytm) testCases.print("Clean Price = ", clean_price) accrued_interest= bond._accruedInterest testCases.print("Accrued = ", accrued_interest) accddays = bond._accrued_days testCases.print("Accrued Days = ", accddays) duration = bond.dollar_duration(settlement_date, ytm) testCases.print("Dollar Duration = ", duration) modified_duration = bond.modified_duration(settlement_date, ytm) testCases.print("Modified Duration = ", modified_duration) macauley_duration = bond.macauley_duration(settlement_date, ytm) testCases.print("Macauley Duration = ", macauley_duration) conv = bond.convexity_from_ytm(settlement_date, ytm) testCases.print("Convexity = ", conv) ########################################################################## # Page 11 APPLE NOTE SCREENSHOT ########################################################################## testCases.banner("BLOOMBERG APPLE CORP BOND EXAMPLE") settlement_date = Date(21, 7, 2017) issue_date = Date(13, 5, 2012) maturity_date = Date(13, 5, 2022) coupon = 0.027 freq_type = FrequencyTypes.SEMI_ANNUAL accrual_type = DayCountTypes.THIRTY_E_360_ISDA face = 100.0 bond = Bond(issue_date, maturity_date, coupon, freq_type, accrual_type, face) testCases.header("FIELD", "VALUE") clean_price = 101.581564 yld = bond.current_yield(clean_price) testCases.print("Current Yield", yld) ytm = bond.yield_to_maturity(settlement_date, clean_price, FinYTMCalcType.UK_DMO) testCases.print("UK DMO Yield To Maturity", ytm) ytm = bond.yield_to_maturity(settlement_date, clean_price, FinYTMCalcType.US_STREET) testCases.print("US STREET Yield To Maturity", ytm) ytm = bond.yield_to_maturity(settlement_date, clean_price, FinYTMCalcType.US_TREASURY) testCases.print("US TREASURY Yield To Maturity", ytm) full_price = bond.full_price_from_ytm(settlement_date, ytm) testCases.print("Full Price", full_price) clean_price = bond.clean_price_from_ytm(settlement_date, ytm) testCases.print("Clean Price", clean_price) accddays = bond._accrued_days testCases.print("Accrued Days", accddays) accrued_interest= bond._accruedInterest testCases.print("Accrued", accrued_interest) duration = bond.dollar_duration(settlement_date, ytm) testCases.print("Dollar Duration", duration) modified_duration = bond.modified_duration(settlement_date, ytm) testCases.print("Modified Duration", modified_duration) macauley_duration = bond.macauley_duration(settlement_date, ytm) testCases.print("Macauley Duration", macauley_duration) conv = bond.convexity_from_ytm(settlement_date, ytm) testCases.print("Convexity", conv)
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._coupon_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._coupon_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)
def test_HullWhiteBondOption(): # Valuation of a European option on a coupon bearing bond settlement_date = Date(1, 12, 2019) issue_date = Date(1, 12, 2018) 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 ncd > settlement_date: if len(coupon_times) == 0: flow_time = (pcd - settlement_date) / gDaysInYear coupon_times.append(flow_time) coupon_flows.append(cpn) flow_time = (ncd - 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 = 100.0 face = 100.0 y = 0.05 times = np.linspace(0, 10, 21) dfs = np.power(1 + y / 2, -times * 2) sigma = 0.0000001 a = 0.1 model = HWTree(sigma, a, None) # Test convergence num_steps_list = range(50, 500, 50) texp = (expiry_date - settlement_date) / gDaysInYear vJam = model.european_bond_option_jamshidian(texp, strike_price, face, coupon_times, coupon_flows, times, dfs) testCases.banner( "Pricing bond option on tree that goes to bond maturity and one using european bond option tree that goes to expiry." ) testCases.header("NUMSTEPS", "TIME", "EXPIRY_ONLY", "EXPIRY_TREE", "JAMSHIDIAN") for num_time_steps in num_steps_list: start = time.time() model = HWTree(sigma, a, num_time_steps, FinHWEuropeanCalcType.EXPIRY_ONLY) model.build_tree(texp, times, dfs) exercise_type = FinExerciseTypes.EUROPEAN v1 = model.bond_option(texp, strike_price, face, coupon_times, coupon_flows, exercise_type) model = HWTree(sigma, a, num_time_steps, FinHWEuropeanCalcType.EXPIRY_TREE) model.build_tree(texp, times, dfs) v2 = model.bond_option(texp, strike_price, face, coupon_times, coupon_flows, exercise_type) end = time.time() period = end - start testCases.print(num_time_steps, period, v1, v2, vJam) # plt.plot(num_steps_list, treeVector) if 1 == 0: print("RT") print_tree(model._rt, 5) print("BOND") print_tree(model._bond_values, 5) print("OPTION") print_tree(model._option_values, 5)
def test_BDTExampleThree(): # Valuation of a swaption as in Leif Andersen's paper - see Table 1 on # SSRN-id155208.pdf testCases.banner("===================== ANDERSEN PAPER ==============") # This is a sanity check testBlackModelCheck() 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 testCases.header("ExerciseType", "Sigma", "NumSteps", "Texp", "Tmat", "V_Fixed", "V_pay", "V_rec") for exercise_type in [ FinExerciseTypes.EUROPEAN, FinExerciseTypes.BERMUDAN ]: for years_to_maturity in [4.0, 5.0, 10.0, 20.0]: maturity_date = settlement_date.add_years(years_to_maturity) issue_date = Date(maturity_date._d, maturity_date._m, 2000) if years_to_maturity == 4.0 or years_to_maturity == 5.0: sigma = 0.2012 elif years_to_maturity == 10.0: sigma = 0.1522 elif years_to_maturity == 20.0: sigma = 0.1035 for expiryYears in range( int(years_to_maturity / 2) - 1, int(years_to_maturity)): 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._flow_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) testCases.print("%s" % exercise_type, "%9.5f" % sigma, "%9.5f" % num_time_steps, "%9.5f" % expiryYears, "%9.5f" % years_to_maturity, "%9.5f" % price, "%9.2f" % (v['pay'] * 100.0), "%9.2f" % (v['rec'] * 100.0))
def test_BondYieldCurve(): ########################################################################### import pandas as pd path = os.path.join(os.path.dirname(__file__), './data/giltBondPrices.txt') bondDataFrame = pd.read_csv(path, sep='\t') bondDataFrame['mid'] = 0.5 * (bondDataFrame['bid'] + bondDataFrame['ask']) freq_type = FrequencyTypes.SEMI_ANNUAL accrual_type = DayCountTypes.ACT_ACT_ICMA settlement = Date(19, 9, 2012) bonds = [] ylds = [] for _, bond in bondDataFrame.iterrows(): dateString = bond['maturity'] matDatetime = dt.datetime.strptime(dateString, '%d-%b-%y') maturityDt = fromDatetime(matDatetime) issueDt = Date(maturityDt._d, maturityDt._m, 2000) coupon = bond['coupon'] / 100.0 clean_price = bond['mid'] bond = Bond(issueDt, maturityDt, coupon, freq_type, accrual_type) yld = bond.yield_to_maturity(settlement, clean_price) bonds.append(bond) ylds.append(yld) ############################################################################### curveFitMethod = CurveFitPolynomial() fittedCurve1 = BondYieldCurve(settlement, bonds, ylds, curveFitMethod) # fittedCurve1.display("GBP Yield Curve") curveFitMethod = CurveFitPolynomial(5) fittedCurve2 = BondYieldCurve(settlement, bonds, ylds, curveFitMethod) # fittedCurve2.display("GBP Yield Curve") curveFitMethod = CurveFitNelsonSiegel() fittedCurve3 = BondYieldCurve(settlement, bonds, ylds, curveFitMethod) # fittedCurve3.display("GBP Yield Curve") curveFitMethod = CurveFitNelsonSiegelSvensson() fittedCurve4 = BondYieldCurve(settlement, bonds, ylds, curveFitMethod) # fittedCurve4.display("GBP Yield Curve") curveFitMethod = CurveFitBSpline() fittedCurve5 = BondYieldCurve(settlement, bonds, ylds, curveFitMethod) # fittedCurve5.display("GBP Yield Curve") ############################################################################### testCases.header("PARAMETER", "VALUE") testCases.print("beta1", fittedCurve3._curveFit._beta1) testCases.print("beta2", fittedCurve3._curveFit._beta2) testCases.print("beta3", fittedCurve3._curveFit._beta3) testCases.print("tau", fittedCurve3._curveFit._tau) testCases.header("PARAMETER", "VALUE") testCases.print("beta1", fittedCurve4._curveFit._beta1) testCases.print("beta2", fittedCurve4._curveFit._beta2) testCases.print("beta3", fittedCurve4._curveFit._beta3) testCases.print("beta4", fittedCurve4._curveFit._beta4) testCases.print("tau1", fittedCurve4._curveFit._tau1) testCases.print("tau2", fittedCurve4._curveFit._tau2) ############################################################################### maturity_date = Date(19, 9, 2030) interpolatedYield = fittedCurve5.interpolatedYield(maturity_date) testCases.print(maturity_date, interpolatedYield)
def test_BondFuture(): # Example taken from Martellini and Priaulet page 360 freq = FrequencyTypes.SEMI_ANNUAL basis = DayCountTypes.ACT_ACT_ICMA issue_date = Date(15, 2, 2004) bond1 = Bond(issue_date, Date(15, 8, 2011), 0.0500, freq, basis) bond2 = Bond(issue_date, Date(15, 2, 2011), 0.0500, freq, basis) bond3 = Bond(issue_date, Date(15, 8, 2010), 0.0575, freq, basis) bond4 = Bond(issue_date, Date(15, 2, 2010), 0.0650, freq, basis) bond5 = Bond(issue_date, Date(15, 8, 2009), 0.0600, freq, basis) bond6 = Bond(issue_date, Date(15, 5, 2009), 0.0550, freq, basis) bond7 = Bond(issue_date, Date(15, 11, 2008), 0.0475, freq, basis) bonds = [] bonds.append(bond1) bonds.append(bond2) bonds.append(bond3) bonds.append(bond4) bonds.append(bond5) bonds.append(bond6) bonds.append(bond7) first_delivery_date = Date(1, 3, 2002) last_delivery_date = Date(28, 3, 2002) contract_size = 100000 contractCoupon = 0.06 bondFutureContract = BondFuture("TYH2", first_delivery_date, last_delivery_date, contract_size, contractCoupon) settlement_date = Date(10, 12, 2001) # Get the Conversion Factors testCases.header("Bond Maturity", "Coupon", "Conversion Factor") for bond in bonds: cf = bondFutureContract.conversion_factor(bond) testCases.print(bond._maturity_date, bond._coupon * 100, cf) # Example from # https://www.cmegroup.com/education/files/understanding-treasury-futures.pdf testCases.banner("EXAMPLE FROM CME") testCases.banner("================") settlement_date = Date(10, 10, 2017) bonds = [] prices = [] bond = Bond(issue_date, Date(15, 8, 2027), 0.0225, freq, basis) bonds.append(bond) prices.append(99 + 1 / 32) bond = Bond(issue_date, Date(15, 5, 2027), 0.02375, freq, basis) bonds.append(bond) prices.append(100 + 5 / 32 + 1 / 64) bond = Bond(issue_date, Date(15, 2, 2027), 0.0225, freq, basis) bonds.append(bond) prices.append(99 + 5 / 32 + 1 / 64) bond = Bond(issue_date, Date(15, 11, 2026), 0.02, freq, basis) bonds.append(bond) prices.append(97 + 7 / 32 + 1 / 64) bond = Bond(issue_date, Date(15, 8, 2026), 0.015, freq, basis) bonds.append(bond) prices.append(93 + 14 / 32) bond = Bond(issue_date, Date(15, 5, 2026), 0.01625, freq, basis) bonds.append(bond) prices.append(94 + 21 / 32 + 1 / 64) bond = Bond(issue_date, Date(15, 2, 2026), 0.01625, freq, basis) bonds.append(bond) prices.append(94 + 29 / 32) bond = Bond(issue_date, Date(15, 11, 2025), 0.0225, freq, basis) bonds.append(bond) prices.append(99 + 25 / 32) bond = Bond(issue_date, Date(15, 8, 2025), 0.02, freq, basis) bonds.append(bond) prices.append(98 + 3 / 32) bond = Bond(issue_date, Date(15, 5, 2025), 0.02125, freq, basis) bonds.append(bond) prices.append(99 + 5 / 32 + 1 / 64) bond = Bond(issue_date, Date(15, 2, 2025), 0.02, freq, basis) bonds.append(bond) prices.append(98 + 14 / 32 + 1 / 64) bond = Bond(issue_date, Date(15, 11, 2024), 0.0225, freq, basis) bonds.append(bond) prices.append(100 + 9 / 32 + 1 / 64) bond = Bond(issue_date, Date(15, 8, 2024), 0.02375, freq, basis) bonds.append(bond) prices.append(101 + 7 / 32 + 1 / 64) bond = Bond(issue_date, Date(15, 8, 2024), 0.01875, freq, basis) bonds.append(bond) # There may be an error in the document says 98-01+ prices.append(98 + 1 / 32) testCases.header("BOND MATURITY", "YIELD") for bond, clean_price in zip(bonds, prices): yld = bond.yield_to_maturity(settlement_date, clean_price) testCases.print(str(bond._maturity_date), yld) first_delivery_date = Date(1, 12, 2017) last_delivery_date = Date(28, 12, 2017) contract_size = 100000 contractCoupon = 0.06 bondFutureContract = BondFuture("TYZ7", first_delivery_date, last_delivery_date, contract_size, contractCoupon) testCases.header("BOND MATURITY", "CF") for bond in bonds: cf = bondFutureContract.conversion_factor(bond) testCases.print(str(bond._maturity_date), cf) # Get the Invoice Prices futures_price = 125.265625 testCases.header("BOND MATURITY", "PRINCIPAL INVOICE PRICE") for bond in bonds: pip = bondFutureContract.principal_invoice_price(bond, futures_price) testCases.print(str(bond._maturity_date), pip) testCases.header("BOND MATURITY", "TOTAL INVOICE AMOUNT") for bond in bonds: tia = bondFutureContract.total_invoice_amount(settlement_date, bond, futures_price) testCases.print(str(bond._maturity_date), tia) ctd = bondFutureContract.cheapest_to_deliver(bonds, prices, futures_price) testCases.header("CTD MATURITY", "CTD COUPON") testCases.print(str(ctd._maturity_date), ctd._coupon)
def test_BondEmbeddedOptionMATLAB(): # https://fr.mathworks.com/help/fininst/optembndbybk.html # I FIND THAT THE PRICE CONVERGES TO 102.365 WHICH IS CLOSE TO 102.382 # FOUND BY MATLAB ALTHOUGH THEY DO NOT EXAMINE THE ASYMPTOTIC PRICE # WHICH MIGHT BE A BETTER MATCH - ALSO THEY DO NOT USE A REALISTIC VOL valuation_date = Date(1, 1, 2007) settlement_date = valuation_date ########################################################################### fixed_leg_type = SwapTypes.PAY dcType = DayCountTypes.THIRTY_E_360 fixedFreq = FrequencyTypes.ANNUAL swap1 = IborSwap(settlement_date, "1Y", fixed_leg_type, 0.0350, fixedFreq, dcType) swap2 = IborSwap(settlement_date, "2Y", fixed_leg_type, 0.0400, fixedFreq, dcType) swap3 = IborSwap(settlement_date, "3Y", fixed_leg_type, 0.0450, fixedFreq, dcType) swaps = [swap1, swap2, swap3] discount_curve = IborSingleCurve(valuation_date, [], [], swaps) ########################################################################### issue_date = Date(1, 1, 2005) maturity_date = Date(1, 1, 2010) coupon = 0.0525 freq_type = FrequencyTypes.ANNUAL accrual_type = DayCountTypes.ACT_ACT_ICMA bond = Bond(issue_date, maturity_date, coupon, freq_type, accrual_type) call_dates = [] call_prices = [] put_dates = [] put_prices = [] putDate = Date(1, 1, 2008) for _ in range(0, 24): put_dates.append(putDate) put_prices.append(100) putDate = putDate.add_months(1) testCases.header("BOND PRICE", "PRICE") v = bond.clean_price_from_discount_curve(settlement_date, discount_curve) testCases.print("Bond Pure Price:", v) sigma = 0.01 # This volatility is very small for a BK process a = 0.1 puttableBond = BondEmbeddedOption(issue_date, maturity_date, coupon, freq_type, accrual_type, call_dates, call_prices, put_dates, put_prices) testCases.header("TIME", "NumTimeSteps", "BondWithOption", "BondPure") timeSteps = range(100, 200, 10) # 1000, 10) values = [] for num_time_steps in timeSteps: model = BKTree(sigma, a, num_time_steps) start = time.time() v = puttableBond.value(settlement_date, discount_curve, model) end = time.time() period = end - start testCases.print(period, num_time_steps, v['bondwithoption'], v['bondpure']) values.append(v['bondwithoption']) if plotGraphs: plt.figure() plt.plot(timeSteps, values)
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.add_weekdays(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.add_months(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/0.035 # 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, 200, 20) # 1000, 10) values = [] for num_time_steps in timeSteps: model = BKTree(sigma, a, num_time_steps) start = time.time() v = puttableBond.value(settlement_date, discount_curve, model) end = time.time() period = end - start testCases.print(period, num_time_steps, v['bondwithoption'], v['bondpure']) values.append(v['bondwithoption']) if plotGraphs: plt.figure() plt.title("Puttable Bond Price Convergence") plt.plot(timeSteps, values)
def test_BKExampleTwo(): # Valuation of a European option on a coupon bearing bond # This follows example in Fig 28.11 of John Hull's book but does not # have the exact same dt so there are some differences settlement_date = Date(1, 12, 2019) issue_date = Date(1, 12, 2018) 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 a = 0.05 num_time_steps = 26 model = BKTree(sigma, a, num_time_steps) model.build_tree(tmat, times, dfs) exercise_type = FinExerciseTypes.AMERICAN v = model.bond_option(texp, strike_price, face, coupon_times, coupon_flows, exercise_type) # Test convergence num_time_steps = 200 exercise_type = FinExerciseTypes.AMERICAN treeVector = [] model = BKTree(sigma, a, num_time_steps) model.build_tree(tmat, times, dfs) v = model.bond_option(texp, strike_price, face, coupon_times, coupon_flows, exercise_type) treeVector.append(v) assert round(v['call'], 4) == 0.6998 assert round(v['put'], 4) == 7.9605
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) tmat = (maturity_date - settlement_date) / gDaysInYear times = np.linspace(0, tmat, 20) dates = settlement_date.add_years(times) dfs = np.exp(-0.05*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 = OptionTypes.EUROPEAN_CALL testCases.header("LABEL", "VALUE") price = bond.full_price_from_discount_curve( settlement_date, discount_curve) testCases.print("Fixed Income Price:", price) num_time_steps = 20 testCases.header("OPTION TYPE AND MODEL", "STRIKE", "VALUE") for strike_price in strikes: sigma = 0.20 a = 0.1 bond_option = BondOption( bond, expiry_date, strike_price, face, option_type) model = BKTree(sigma, a, num_time_steps) v = bond_option.value(settlement_date, discount_curve, model) testCases.print("EUROPEAN CALL - BK", strike_price, v) for strike_price in strikes: sigma = 0.20 a = 0.05 bond_option = BondOption( bond, expiry_date, strike_price, face, option_type) model = BKTree(sigma, a, num_time_steps) v = bond_option.value(settlement_date, discount_curve, model) testCases.print("EUROPEAN CALL - BK", strike_price, v) ########################################################################### option_type = OptionTypes.AMERICAN_CALL price = bond.full_price_from_discount_curve( settlement_date, discount_curve) testCases.header("LABEL", "VALUE") testCases.print("Fixed Income Price:", price) testCases.header("OPTION TYPE AND MODEL", "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 = BKTree(sigma, a) v = bond_option.value(settlement_date, discount_curve, model) testCases.print("AMERICAN CALL - BK", strike_price, v) for strike_price in strikes: sigma = 0.20 a = 0.05 bond_option = BondOption( bond, expiry_date, strike_price, face, option_type) model = BKTree(sigma, a) v = bond_option.value(settlement_date, discount_curve, model) testCases.print("AMERICAN CALL - BK", strike_price, v) ########################################################################### option_type = OptionTypes.EUROPEAN_PUT price = bond.full_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 = BKTree(sigma, a) v = bond_option.value(settlement_date, discount_curve, model) testCases.print("EUROPEAN PUT - BK", strike_price, v) for strike_price in strikes: sigma = 0.20 a = 0.05 bond_option = BondOption( bond, expiry_date, strike_price, face, option_type) model = BKTree(sigma, a) v = bond_option.value(settlement_date, discount_curve, model) testCases.print("EUROPEAN PUT - BK", strike_price, v) ########################################################################### option_type = OptionTypes.AMERICAN_PUT price = bond.full_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 = BKTree(sigma, a) v = bond_option.value(settlement_date, discount_curve, model) testCases.print("AMERICAN PUT - BK", strike_price, v) for strike_price in strikes: sigma = 0.20 a = 0.05 bond_option = BondOption( bond, expiry_date, strike_price, face, option_type) model = BKTree(sigma, a) v = bond_option.value(settlement_date, discount_curve, model) testCases.print("AMERICAN PUT - BK", strike_price, v)
def test_BondOptionAmericanConvergenceTWO(): # Build discount curve settlement_date = Date(1, 12, 2019) discount_curve = DiscountCurveFlat(settlement_date, 0.05, FrequencyTypes.CONTINUOUS) # Bond details issue_date = Date(1, 9, 2014) maturity_date = Date(1, 9, 2025) coupon = 0.05 freq_type = FrequencyTypes.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.full_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.2 a = 0.1 bkModel = BKTree(sigma, a) K = 101.0 vec_ec = [] vec_ac = [] vec_ep = [] vec_ap = [] if 1 == 1: K = 100.0 bkModel = BKTree(sigma, a, 100) europeanCallBondOption = BondOption(bond, expiry_date, K, face, OptionTypes.EUROPEAN_CALL) v_ec = europeanCallBondOption.value(settlement_date, discount_curve, bkModel) testCases.header("LABEL", "VALUE") testCases.print("OPTION", v_ec) num_stepsVector = range(100, 100, 1) # should be 100-400 for num_steps in num_stepsVector: bkModel = BKTree(sigma, a, num_steps) start = time.time() europeanCallBondOption = BondOption(bond, expiry_date, K, face, OptionTypes.EUROPEAN_CALL) v_ec = europeanCallBondOption.value(settlement_date, discount_curve, bkModel) americanCallBondOption = BondOption(bond, expiry_date, K, face, OptionTypes.AMERICAN_CALL) v_ac = americanCallBondOption.value(settlement_date, discount_curve, bkModel) europeanPutBondOption = BondOption(bond, expiry_date, K, face, OptionTypes.EUROPEAN_PUT) v_ep = europeanPutBondOption.value(settlement_date, discount_curve, bkModel) americanPutBondOption = BondOption(bond, expiry_date, K, face, OptionTypes.AMERICAN_PUT) v_ap = americanPutBondOption.value(settlement_date, discount_curve, bkModel) 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_ec, label="European Call") plt.legend() plt.figure() plt.plot(num_stepsVector, vec_ac, label="American Call") plt.legend() plt.figure() plt.plot(num_stepsVector, vec_ep, label="European Put") plt.legend() plt.figure() plt.plot(num_stepsVector, vec_ap, label="American Put") plt.legend()
def test_BondOptionEuropeanConvergence(): # CONVERGENCE TESTS # COMPARE AMERICAN TREE VERSUS JAMSHIDIAN IN EUROPEAN LIMIT TO CHECK THAT # TREE HAS BEEN CORRECTLY CONSTRUCTED. FIND VERY GOOD AGREEMENT. # Build discount curve settlement_date = Date(1, 12, 2019) discount_curve = DiscountCurveFlat(settlement_date, 0.05, FrequencyTypes.CONTINUOUS) # Bond details issue_date = Date(1, 12, 2015) maturity_date = Date(1, 12, 2020) coupon = 0.05 freq_type = FrequencyTypes.ANNUAL accrual_type = DayCountTypes.ACT_ACT_ICMA bond = Bond(issue_date, maturity_date, coupon, freq_type, accrual_type) # Option Details - put expiry in the middle of a coupon period expiry_date = Date(1, 3, 2020) strike_price = 100.0 face = 100.0 timeSteps = range(100, 400, 100) strike_price = 100.0 testCases.header("TIME", "N", "PUT_JAM", "PUT_TREE", "CALL_JAM", "CALL_TREE") for num_time_steps in timeSteps: sigma = 0.05 a = 0.1 start = time.time() option_type = OptionTypes.EUROPEAN_PUT bond_option1 = BondOption(bond, expiry_date, strike_price, face, option_type) model1 = HWTree(sigma, a, num_time_steps) v1put = bond_option1.value(settlement_date, discount_curve, model1) bond_option2 = BondOption(bond, expiry_date, strike_price, face, option_type) model2 = HWTree(sigma, a, num_time_steps, FinHWEuropeanCalcType.EXPIRY_ONLY) v2put = bond_option2.value(settlement_date, discount_curve, model2) option_type = OptionTypes.EUROPEAN_CALL bond_option1 = BondOption(bond, expiry_date, strike_price, face, option_type) model1 = HWTree(sigma, a, num_time_steps) v1call = bond_option1.value(settlement_date, discount_curve, model1) bond_option2 = BondOption(bond, expiry_date, strike_price, face, option_type) model2 = HWTree(sigma, a, num_time_steps, FinHWEuropeanCalcType.EXPIRY_TREE) v2call = bond_option2.value(settlement_date, discount_curve, model2) end = time.time() period = end - start testCases.print(period, num_time_steps, v1put, v2put, v1call, v2call)
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, 1000, 100) strike_prices = [90, 100, 110, 120] 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)
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._flow_dates) for i in range(0, numFlows): pcd = bond._flow_dates[i - 1] ncd = bond._flow_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)