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(20, 100, 20) 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 testIborSwaptionModels(): ########################################################################## # COMPARISON OF MODELS ########################################################################## valuation_date = Date(1, 1, 2011) libor_curve = test_ibor_depositsAndSwaps(valuation_date) exercise_date = Date(1, 1, 2012) swapMaturityDate = Date(1, 1, 2017) swapFixedFrequencyType = FrequencyTypes.SEMI_ANNUAL swapFixedDayCountType = DayCountTypes.ACT_365F strikes = np.linspace(0.02, 0.08, 5) testCases.header("LAB", "STRIKE", "BLK", "BLK_SHFT", "SABR", "SABR_SHFT", "HW", "BK") model1 = Black(0.00001) model2 = BlackShifted(0.00001, 0.0) model3 = SABR(0.013, 0.5, 0.5, 0.5) model4 = SABRShifted(0.013, 0.5, 0.5, 0.5, -0.008) model5 = HWTree(0.00001, 0.00001) model6 = BKTree(0.01, 0.01) settlement_date = valuation_date.add_weekdays(2) for k in strikes: swaptionType = SwapTypes.PAY swaption = IborSwaption(settlement_date, exercise_date, swapMaturityDate, swaptionType, k, swapFixedFrequencyType, swapFixedDayCountType) swap1 = swaption.value(valuation_date, libor_curve, model1) swap2 = swaption.value(valuation_date, libor_curve, model2) swap3 = swaption.value(valuation_date, libor_curve, model3) swap4 = swaption.value(valuation_date, libor_curve, model4) swap5 = swaption.value(valuation_date, libor_curve, model5) swap6 = swaption.value(valuation_date, libor_curve, model6) testCases.print("PAY", k, swap1, swap2, swap3, swap4, swap5, swap6) testCases.header("LABEL", "STRIKE", "BLK", "BLK_SHFTD", "SABR", "SABR_SHFTD", "HW", "BK") for k in strikes: swaptionType = SwapTypes.RECEIVE swaption = IborSwaption(settlement_date, exercise_date, swapMaturityDate, swaptionType, k, swapFixedFrequencyType, swapFixedDayCountType) swap1 = swaption.value(valuation_date, libor_curve, model1) swap2 = swaption.value(valuation_date, libor_curve, model2) swap3 = swaption.value(valuation_date, libor_curve, model3) swap4 = swaption.value(valuation_date, libor_curve, model4) swap5 = swaption.value(valuation_date, libor_curve, model5) swap6 = swaption.value(valuation_date, libor_curve, model6) testCases.print("REC", k, swap1, swap2, swap3, swap4, swap5, swap6)
def test_matlab_bk(): sigma = 0.01 # This volatility is very small for a BK process a = 0.1 model = BKTree(sigma, a, num_time_steps) v = puttableBond_matlab.value(settlement_date_matlab, discount_curve_matlab, model) assert round(v['bondwithoption'], 4) == 102.3508 assert round(v['bondpure'], 4) == 102.0603
def test_quantlib_bk(): sigma = 0.12 / 0.035 # basis point volatility a = 0.03 model = BKTree(sigma, a, num_time_steps) v = puttableBond_quantlib.value(settlement_date_quantlib, discount_curve_quantlib, model) assert round(v['bondwithoption'], 4) == 89.7614 assert round(v['bondpure'], 4) == 95.0619
def test_bk_bermudan_exercise(): fixed_leg_type = SwapTypes.PAY exercise_type = FinExerciseTypes.BERMUDAN bermudan_swaption_pay = IborBermudanSwaption( settlement_date, exercise_date, swapMaturityDate, fixed_leg_type, exercise_type, swapFixedCoupon, swapFixedFrequencyType, swapFixedDayCountType) fixed_leg_type = SwapTypes.RECEIVE exercise_type = FinExerciseTypes.BERMUDAN bermudan_swaption_rec = IborBermudanSwaption( settlement_date, exercise_date, swapMaturityDate, fixed_leg_type, exercise_type, swapFixedCoupon, swapFixedFrequencyType, swapFixedDayCountType) # Used BK with constant short-rate volatility sigma = 0.000001 a = 0.01 model = BKTree(sigma, a, num_time_steps) valuePay = bermudan_swaption_pay.value(valuation_date, libor_curve, model) assert round(valuePay, 4) == 6313.7455 valueRec = bermudan_swaption_rec.value(valuation_date, libor_curve, model) assert valueRec == 0.0 # Used BK with constant short-rate volatility sigma = 0.20 a = 0.01 model = BKTree(sigma, a, num_time_steps) valuePay = bermudan_swaption_pay.value(valuation_date, libor_curve, model) assert round(valuePay, 4) == 19175.5406 valueRec = bermudan_swaption_rec.value(valuation_date, libor_curve, model) assert round(valueRec, 4) == 12956.6057
def test_american_put_bk(): option_type = OptionTypes.AMERICAN_PUT strike_price = 100 bond_option = BondOption(bond, expiry_date, strike_price, face, option_type) sigma = 0.02 a = 0.1 model = BKTree(sigma, a) v = bond_option.value(settlement_date, discount_curve, model) assert round(v, 4) == 0.5331
def test_european_call_bk(): option_type = OptionTypes.EUROPEAN_CALL strike_price = 100 bond_option = BondOption(bond, expiry_date, strike_price, face, option_type) sigma = 0.20 a = 0.1 num_time_steps = 20 model = BKTree(sigma, a, num_time_steps) v = bond_option.value(settlement_date, discount_curve, model) assert round(v, 4) == 1.7055
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_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 testIborSwaptionMatlabExamples(): # We value a European swaption using Black's model and try to replicate a # ML example at https://fr.mathworks.com/help/fininst/swaptionbyblk.html testCases.header("=======================================") testCases.header("MATLAB EXAMPLE WITH FLAT TERM STRUCTURE") testCases.header("=======================================") valuation_date = Date(1, 1, 2010) libor_curve = DiscountCurveFlat(valuation_date, 0.06, FrequencyTypes.CONTINUOUS, DayCountTypes.THIRTY_E_360) settlement_date = Date(1, 1, 2011) exercise_date = Date(1, 1, 2016) maturity_date = Date(1, 1, 2019) fixed_coupon = 0.062 fixed_frequency_type = FrequencyTypes.SEMI_ANNUAL fixed_day_count_type = DayCountTypes.THIRTY_E_360_ISDA notional = 100.0 # Pricing a PAY swaptionType = SwapTypes.PAY swaption = IborSwaption(settlement_date, exercise_date, maturity_date, swaptionType, fixed_coupon, fixed_frequency_type, fixed_day_count_type, notional) model = Black(0.20) v_finpy = swaption.value(valuation_date, libor_curve, model) v_matlab = 2.071 testCases.header("LABEL", "VALUE") testCases.print("FP Price:", v_finpy) testCases.print("MATLAB Prix:", v_matlab) testCases.print("DIFF:", v_finpy - v_matlab) ############################################################################### testCases.header("===================================") testCases.header("MATLAB EXAMPLE WITH TERM STRUCTURE") testCases.header("===================================") valuation_date = Date(1, 1, 2010) dates = [ Date(1, 1, 2011), Date(1, 1, 2012), Date(1, 1, 2013), Date(1, 1, 2014), Date(1, 1, 2015) ] zero_rates = [0.03, 0.034, 0.037, 0.039, 0.040] contFreq = FrequencyTypes.CONTINUOUS interp_type = InterpTypes.LINEAR_ZERO_RATES day_count_type = DayCountTypes.THIRTY_E_360 libor_curve = DiscountCurveZeros(valuation_date, dates, zero_rates, contFreq, day_count_type, interp_type) settlement_date = Date(1, 1, 2011) exercise_date = Date(1, 1, 2012) maturity_date = Date(1, 1, 2017) fixed_coupon = 0.03 fixed_frequency_type = FrequencyTypes.SEMI_ANNUAL fixed_day_count_type = DayCountTypes.THIRTY_E_360 float_frequency_type = FrequencyTypes.SEMI_ANNUAL float_day_count_type = DayCountTypes.THIRTY_E_360 notional = 1000.0 # Pricing a put swaptionType = SwapTypes.RECEIVE swaption = IborSwaption(settlement_date, exercise_date, maturity_date, swaptionType, fixed_coupon, fixed_frequency_type, fixed_day_count_type, notional, float_frequency_type, float_day_count_type) model = Black(0.21) v_finpy = swaption.value(valuation_date, libor_curve, model) v_matlab = 0.5771 testCases.header("LABEL", "VALUE") testCases.print("FP Price:", v_finpy) testCases.print("MATLAB Prix:", v_matlab) testCases.print("DIFF:", v_finpy - v_matlab) ############################################################################### testCases.header("===================================") testCases.header("MATLAB EXAMPLE WITH SHIFTED BLACK") testCases.header("===================================") valuation_date = Date(1, 1, 2016) dates = [ Date(1, 1, 2017), Date(1, 1, 2018), Date(1, 1, 2019), Date(1, 1, 2020), Date(1, 1, 2021) ] zero_rates = np.array([-0.02, 0.024, 0.047, 0.090, 0.12]) / 100.0 contFreq = FrequencyTypes.ANNUAL interp_type = InterpTypes.LINEAR_ZERO_RATES day_count_type = DayCountTypes.THIRTY_E_360 libor_curve = DiscountCurveZeros(valuation_date, dates, zero_rates, contFreq, day_count_type, interp_type) settlement_date = Date(1, 1, 2016) exercise_date = Date(1, 1, 2017) maturity_date = Date(1, 1, 2020) fixed_coupon = -0.003 fixed_frequency_type = FrequencyTypes.SEMI_ANNUAL fixed_day_count_type = DayCountTypes.THIRTY_E_360_ISDA float_frequency_type = FrequencyTypes.SEMI_ANNUAL float_day_count_type = DayCountTypes.THIRTY_E_360_ISDA notional = 1000.0 # Pricing a PAY swaptionType = SwapTypes.PAY swaption = IborSwaption(settlement_date, exercise_date, maturity_date, swaptionType, fixed_coupon, fixed_frequency_type, fixed_day_count_type, notional, float_frequency_type, float_day_count_type) model = BlackShifted(0.31, 0.008) v_finpy = swaption.value(valuation_date, libor_curve, model) v_matlab = 12.8301 testCases.header("LABEL", "VALUE") testCases.print("FP Price:", v_finpy) testCases.print("MATLAB Prix:", v_matlab) testCases.print("DIFF:", v_finpy - v_matlab) ############################################################################### testCases.header("===================================") testCases.header("MATLAB EXAMPLE WITH HULL WHITE") testCases.header("===================================") # https://fr.mathworks.com/help/fininst/swaptionbyhw.html valuation_date = Date(1, 1, 2007) dates = [ Date(1, 1, 2007), Date(1, 7, 2007), Date(1, 1, 2008), Date(1, 7, 2008), Date(1, 1, 2009), Date(1, 7, 2009), Date(1, 1, 2010), Date(1, 7, 2010), Date(1, 1, 2011), Date(1, 7, 2011), Date(1, 1, 2012) ] zero_rates = np.array([0.075] * 11) interp_type = InterpTypes.FLAT_FWD_RATES day_count_type = DayCountTypes.THIRTY_E_360_ISDA contFreq = FrequencyTypes.SEMI_ANNUAL libor_curve = DiscountCurveZeros(valuation_date, dates, zero_rates, contFreq, day_count_type, interp_type) settlement_date = valuation_date exercise_date = Date(1, 1, 2010) maturity_date = Date(1, 1, 2012) fixed_coupon = 0.04 fixed_frequency_type = FrequencyTypes.SEMI_ANNUAL fixed_day_count_type = DayCountTypes.THIRTY_E_360_ISDA notional = 100.0 swaptionType = SwapTypes.RECEIVE swaption = IborSwaption(settlement_date, exercise_date, maturity_date, swaptionType, fixed_coupon, fixed_frequency_type, fixed_day_count_type, notional) model = HWTree(0.05, 0.01) v_finpy = swaption.value(valuation_date, libor_curve, model) v_matlab = 2.9201 testCases.header("LABEL", "VALUE") testCases.print("FP Price:", v_finpy) testCases.print("MATLAB Prix:", v_matlab) testCases.print("DIFF:", v_finpy - v_matlab) ############################################################################### testCases.header("====================================") testCases.header("MATLAB EXAMPLE WITH BLACK KARASINSKI") testCases.header("====================================") # https://fr.mathworks.com/help/fininst/swaptionbybk.html valuation_date = Date(1, 1, 2007) dates = [ Date(1, 1, 2007), Date(1, 7, 2007), Date(1, 1, 2008), Date(1, 7, 2008), Date(1, 1, 2009), Date(1, 7, 2009), Date(1, 1, 2010), Date(1, 7, 2010), Date(1, 1, 2011), Date(1, 7, 2011), Date(1, 1, 2012) ] zero_rates = np.array([0.07] * 11) interp_type = InterpTypes.FLAT_FWD_RATES day_count_type = DayCountTypes.THIRTY_E_360_ISDA contFreq = FrequencyTypes.SEMI_ANNUAL libor_curve = DiscountCurveZeros(valuation_date, dates, zero_rates, contFreq, day_count_type, interp_type) settlement_date = valuation_date exercise_date = Date(1, 1, 2011) maturity_date = Date(1, 1, 2012) fixed_frequency_type = FrequencyTypes.SEMI_ANNUAL fixed_day_count_type = DayCountTypes.THIRTY_E_360_ISDA notional = 100.0 model = BKTree(0.1, 0.05, 200) fixed_coupon = 0.07 swaptionType = SwapTypes.PAY swaption = IborSwaption(settlement_date, exercise_date, maturity_date, swaptionType, fixed_coupon, fixed_frequency_type, fixed_day_count_type, notional) v_finpy = swaption.value(valuation_date, libor_curve, model) v_matlab = 0.3634 testCases.header("LABEL", "VALUE") testCases.print("FP Price:", v_finpy) testCases.print("MATLAB Prix:", v_matlab) testCases.print("DIFF:", v_finpy - v_matlab) fixed_coupon = 0.0725 swaptionType = SwapTypes.RECEIVE swaption = IborSwaption(settlement_date, exercise_date, maturity_date, swaptionType, fixed_coupon, fixed_frequency_type, fixed_day_count_type, notional) v_finpy = swaption.value(valuation_date, libor_curve, model) v_matlab = 0.4798 testCases.header("LABEL", "VALUE") testCases.print("FP Price:", v_finpy) testCases.print("MATLAB Prix:", v_matlab) testCases.print("DIFF:", v_finpy - v_matlab) ############################################################################### testCases.header("====================================") testCases.header("MATLAB EXAMPLE WITH BLACK-DERMAN-TOY") testCases.header("====================================") # https://fr.mathworks.com/help/fininst/swaptionbybdt.html valuation_date = Date(1, 1, 2007) dates = [ Date(1, 1, 2007), Date(1, 7, 2007), Date(1, 1, 2008), Date(1, 7, 2008), Date(1, 1, 2009), Date(1, 7, 2009), Date(1, 1, 2010), Date(1, 7, 2010), Date(1, 1, 2011), Date(1, 7, 2011), Date(1, 1, 2012) ] zero_rates = np.array([0.06] * 11) interp_type = InterpTypes.FLAT_FWD_RATES day_count_type = DayCountTypes.THIRTY_E_360_ISDA contFreq = FrequencyTypes.ANNUAL libor_curve = DiscountCurveZeros(valuation_date, dates, zero_rates, contFreq, day_count_type, interp_type) settlement_date = valuation_date exercise_date = Date(1, 1, 2012) maturity_date = Date(1, 1, 2015) fixed_frequency_type = FrequencyTypes.ANNUAL fixed_day_count_type = DayCountTypes.THIRTY_E_360_ISDA notional = 100.0 fixed_coupon = 0.062 swaptionType = SwapTypes.PAY swaption = IborSwaption(settlement_date, exercise_date, maturity_date, swaptionType, fixed_coupon, fixed_frequency_type, fixed_day_count_type, notional) model = BDTTree(0.20, 200) v_finpy = swaption.value(valuation_date, libor_curve, model) v_matlab = 2.0592 testCases.header("LABEL", "VALUE") testCases.print("FP Price:", v_finpy) testCases.print("MATLAB Prix:", v_matlab) testCases.print("DIFF:", v_finpy - v_matlab)
def test_IborBermudanSwaptionBKModel(): """ Replicate examples in paper by Leif Andersen which can be found at file:///C:/Users/Dominic/Downloads/SSRN-id155208.pdf """ valuation_date = Date(1, 1, 2011) settlement_date = valuation_date exercise_date = settlement_date.add_years(1) swapMaturityDate = settlement_date.add_years(4) swapFixedCoupon = 0.060 swapFixedFrequencyType = FrequencyTypes.SEMI_ANNUAL swapFixedDayCountType = DayCountTypes.ACT_365F libor_curve = DiscountCurveFlat(valuation_date, 0.0625, FrequencyTypes.SEMI_ANNUAL, DayCountTypes.ACT_365F) fwdPAYSwap = IborSwap(exercise_date, swapMaturityDate, SwapTypes.PAY, swapFixedCoupon, swapFixedFrequencyType, swapFixedDayCountType) fwdSwapValue = fwdPAYSwap.value(settlement_date, libor_curve, libor_curve) testCases.header("LABEL", "VALUE") testCases.print("FWD SWAP VALUE", fwdSwapValue) # fwdPAYSwap.print_fixed_leg_pv() # Now we create the European swaptions fixed_leg_type = SwapTypes.PAY europeanSwaptionPay = IborSwaption(settlement_date, exercise_date, swapMaturityDate, fixed_leg_type, swapFixedCoupon, swapFixedFrequencyType, swapFixedDayCountType) fixed_leg_type = SwapTypes.RECEIVE europeanSwaptionRec = IborSwaption(settlement_date, exercise_date, swapMaturityDate, fixed_leg_type, swapFixedCoupon, swapFixedFrequencyType, swapFixedDayCountType) ########################################################################### ########################################################################### ########################################################################### # BLACK'S MODEL ########################################################################### ########################################################################### ########################################################################### testCases.banner("======= ZERO VOLATILITY ========") model = Black(0.0000001) testCases.print("Black Model", model._volatility) valuePay = europeanSwaptionPay.value(settlement_date, libor_curve, model) testCases.print("EUROPEAN BLACK PAY VALUE ZERO VOL:", valuePay) valueRec = europeanSwaptionRec.value(settlement_date, libor_curve, model) testCases.print("EUROPEAN BLACK REC VALUE ZERO VOL:", valueRec) payRec = valuePay - valueRec testCases.print("PAY MINUS RECEIVER :", payRec) testCases.banner("======= 20%% BLACK VOLATILITY ========") model = Black(0.20) testCases.print("Black Model", model._volatility) valuePay = europeanSwaptionPay.value(settlement_date, libor_curve, model) testCases.print("EUROPEAN BLACK PAY VALUE:", valuePay) valueRec = europeanSwaptionRec.value(settlement_date, libor_curve, model) testCases.print("EUROPEAN BLACK REC VALUE:", valueRec) payRec = valuePay - valueRec testCases.print("PAY MINUS RECEIVER :", payRec) ########################################################################### ########################################################################### ########################################################################### # BK MODEL ########################################################################### ########################################################################### ########################################################################### testCases.banner("=======================================================") testCases.banner("=======================================================") testCases.banner("==================== BK MODEL =========================") testCases.banner("=======================================================") testCases.banner("=======================================================") testCases.banner("======= 0% VOLATILITY EUROPEAN SWAPTION BK MODEL ======") # Used BK with constant short-rate volatility sigma = 0.000000001 a = 0.01 num_time_steps = 100 model = BKTree(sigma, a, num_time_steps) valuePay = europeanSwaptionPay.value(valuation_date, libor_curve, model) testCases.print("EUROPEAN BK PAY VALUE:", valuePay) valueRec = europeanSwaptionRec.value(valuation_date, libor_curve, model) testCases.print("EUROPEAN BK REC VALUE:", valueRec) payRec = valuePay - valueRec testCases.print("PAY MINUS RECEIVER :", payRec) testCases.banner( "======= 20% VOLATILITY EUROPEAN SWAPTION BK MODEL ========") # Used BK with constant short-rate volatility sigma = 0.20 a = 0.01 model = BKTree(sigma, a, num_time_steps) testCases.banner("BK MODEL SWAPTION CLASS EUROPEAN EXERCISE") valuePay = europeanSwaptionPay.value(valuation_date, libor_curve, model) testCases.print("EUROPEAN BK PAY VALUE:", valuePay) valueRec = europeanSwaptionRec.value(valuation_date, libor_curve, model) testCases.print("EUROPEAN BK REC VALUE:", valueRec) payRec = valuePay - valueRec testCases.print("PAY MINUS RECEIVER :", payRec) ########################################################################### # Now we create the Bermudan swaptions but only allow European exercise fixed_leg_type = SwapTypes.PAY exercise_type = FinExerciseTypes.EUROPEAN bermudan_swaption_pay = IborBermudanSwaption( settlement_date, exercise_date, swapMaturityDate, fixed_leg_type, exercise_type, swapFixedCoupon, swapFixedFrequencyType, swapFixedDayCountType) fixed_leg_type = SwapTypes.RECEIVE exercise_type = FinExerciseTypes.EUROPEAN bermudan_swaption_rec = IborBermudanSwaption( settlement_date, exercise_date, swapMaturityDate, fixed_leg_type, exercise_type, swapFixedCoupon, swapFixedFrequencyType, swapFixedDayCountType) testCases.banner( "======= 0% VOLATILITY BERMUDAN SWAPTION EUROPEAN EXERCISE BK MODEL ========" ) # Used BK with constant short-rate volatility sigma = 0.000001 a = 0.01 model = BKTree(sigma, a, num_time_steps) testCases.banner("BK MODEL BERMUDAN SWAPTION CLASS EUROPEAN EXERCISE") valuePay = bermudan_swaption_pay.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN BK PAY VALUE:", valuePay) valueRec = bermudan_swaption_rec.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN BK REC VALUE:", valueRec) payRec = valuePay - valueRec testCases.print("PAY MINUS RECEIVER :", payRec) testCases.banner( "======= 20% VOLATILITY BERMUDAN SWAPTION EUROPEAN EXERCISE BK MODEL ========" ) # Used BK with constant short-rate volatility sigma = 0.2 a = 0.01 model = BKTree(sigma, a, num_time_steps) testCases.banner("BK MODEL BERMUDAN SWAPTION CLASS EUROPEAN EXERCISE") valuePay = bermudan_swaption_pay.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN BK PAY VALUE:", valuePay) valueRec = bermudan_swaption_rec.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN BK REC VALUE:", valueRec) payRec = valuePay - valueRec testCases.print("PAY MINUS RECEIVER :", payRec) ########################################################################### # Now we create the Bermudan swaptions but allow Bermudan exercise ########################################################################### fixed_leg_type = SwapTypes.PAY exercise_type = FinExerciseTypes.BERMUDAN bermudan_swaption_pay = IborBermudanSwaption( settlement_date, exercise_date, swapMaturityDate, fixed_leg_type, exercise_type, swapFixedCoupon, swapFixedFrequencyType, swapFixedDayCountType) fixed_leg_type = SwapTypes.RECEIVE exercise_type = FinExerciseTypes.BERMUDAN bermudan_swaption_rec = IborBermudanSwaption( settlement_date, exercise_date, swapMaturityDate, fixed_leg_type, exercise_type, swapFixedCoupon, swapFixedFrequencyType, swapFixedDayCountType) testCases.banner( "======= ZERO VOLATILITY BERMUDAN SWAPTION BERMUDAN EXERCISE BK MODEL ========" ) # Used BK with constant short-rate volatility sigma = 0.000001 a = 0.01 model = BKTree(sigma, a, num_time_steps) testCases.banner("BK MODEL BERMUDAN SWAPTION CLASS BERMUDAN EXERCISE") valuePay = bermudan_swaption_pay.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN BK PAY VALUE:", valuePay) valueRec = bermudan_swaption_rec.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN BK REC VALUE:", valueRec) payRec = valuePay - valueRec testCases.print("PAY MINUS RECEIVER :", payRec) testCases.banner( "======= 20% VOLATILITY BERMUDAN SWAPTION BERMUDAN EXERCISE BK MODEL ========" ) # Used BK with constant short-rate volatility sigma = 0.20 a = 0.01 model = BKTree(sigma, a, num_time_steps) testCases.banner("BK MODEL BERMUDAN SWAPTION CLASS BERMUDAN EXERCISE") valuePay = bermudan_swaption_pay.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN BK PAY VALUE:", valuePay) valueRec = bermudan_swaption_rec.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN BK REC VALUE:", valueRec) payRec = valuePay - valueRec testCases.print("PAY MINUS RECEIVER :", payRec) ########################################################################### ########################################################################### ########################################################################### # BDT MODEL ########################################################################### ########################################################################### ########################################################################### testCases.banner("=======================================================") testCases.banner("=======================================================") testCases.banner("======================= BDT MODEL =====================") testCases.banner("=======================================================") testCases.banner("=======================================================") testCases.banner("====== 0% VOLATILITY EUROPEAN SWAPTION BDT MODEL ======") # Used BK with constant short-rate volatility sigma = 0.00001 num_time_steps = 200 model = BDTTree(sigma, num_time_steps) valuePay = europeanSwaptionPay.value(valuation_date, libor_curve, model) testCases.print("EUROPEAN BDT PAY VALUE:", valuePay) valueRec = europeanSwaptionRec.value(valuation_date, libor_curve, model) testCases.print("EUROPEAN BDT REC VALUE:", valueRec) payRec = valuePay - valueRec testCases.print("PAY MINUS RECEIVER :", payRec) testCases.banner("===== 20% VOLATILITY EUROPEAN SWAPTION BDT MODEL ======") # Used BK with constant short-rate volatility sigma = 0.20 a = 0.01 model = BDTTree(sigma, num_time_steps) testCases.banner("BDT MODEL SWAPTION CLASS EUROPEAN EXERCISE") valuePay = europeanSwaptionPay.value(valuation_date, libor_curve, model) testCases.print("EUROPEAN BDT PAY VALUE:", valuePay) valueRec = europeanSwaptionRec.value(valuation_date, libor_curve, model) testCases.print("EUROPEAN BDT REC VALUE:", valueRec) payRec = valuePay - valueRec testCases.print("PAY MINUS RECEIVER :", payRec) ########################################################################### # Now we create the Bermudan swaptions but only allow European exercise fixed_leg_type = SwapTypes.PAY exercise_type = FinExerciseTypes.EUROPEAN bermudan_swaption_pay = IborBermudanSwaption( settlement_date, exercise_date, swapMaturityDate, fixed_leg_type, exercise_type, swapFixedCoupon, swapFixedFrequencyType, swapFixedDayCountType) fixed_leg_type = SwapTypes.RECEIVE bermudan_swaption_rec = IborBermudanSwaption( settlement_date, exercise_date, swapMaturityDate, fixed_leg_type, exercise_type, swapFixedCoupon, swapFixedFrequencyType, swapFixedDayCountType) testCases.banner( "======= 0% VOLATILITY BERMUDAN SWAPTION EUROPEAN EXERCISE BDT MODEL ========" ) # Used BK with constant short-rate volatility sigma = 0.000001 model = BDTTree(sigma, num_time_steps) testCases.banner("BK MODEL BERMUDAN SWAPTION CLASS EUROPEAN EXERCISE") valuePay = bermudan_swaption_pay.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN BDT PAY VALUE:", valuePay) valueRec = bermudan_swaption_rec.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN BDT REC VALUE:", valueRec) payRec = valuePay - valueRec testCases.print("PAY MINUS RECEIVER :", payRec) testCases.banner( "======= 20% VOLATILITY BERMUDAN SWAPTION EUROPEAN EXERCISE BDT MODEL ========" ) # Used BK with constant short-rate volatility sigma = 0.2 model = BDTTree(sigma, num_time_steps) testCases.banner("BDT MODEL BERMUDAN SWAPTION CLASS EUROPEAN EXERCISE") valuePay = bermudan_swaption_pay.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN BDT PAY VALUE:", valuePay) valueRec = bermudan_swaption_rec.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN BDT REC VALUE:", valueRec) payRec = valuePay - valueRec testCases.print("PAY MINUS RECEIVER :", payRec) ########################################################################### # Now we create the Bermudan swaptions but allow Bermudan exercise ########################################################################### fixed_leg_type = SwapTypes.PAY exercise_type = FinExerciseTypes.BERMUDAN bermudan_swaption_pay = IborBermudanSwaption( settlement_date, exercise_date, swapMaturityDate, fixed_leg_type, exercise_type, swapFixedCoupon, swapFixedFrequencyType, swapFixedDayCountType) fixed_leg_type = SwapTypes.RECEIVE bermudan_swaption_rec = IborBermudanSwaption( settlement_date, exercise_date, swapMaturityDate, fixed_leg_type, exercise_type, swapFixedCoupon, swapFixedFrequencyType, swapFixedDayCountType) testCases.banner( "======= ZERO VOLATILITY BERMUDAN SWAPTION BERMUDAN EXERCISE BDT MODEL ========" ) # Used BK with constant short-rate volatility sigma = 0.000001 a = 0.01 model = BDTTree(sigma, num_time_steps) testCases.banner("BK MODEL BERMUDAN SWAPTION CLASS BERMUDAN EXERCISE") valuePay = bermudan_swaption_pay.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN BDT PAY VALUE:", valuePay) valueRec = bermudan_swaption_rec.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN BDT REC VALUE:", valueRec) payRec = valuePay - valueRec testCases.print("PAY MINUS RECEIVER :", payRec) testCases.banner( "======= 20% VOLATILITY BERMUDAN SWAPTION BERMUDAN EXERCISE BDT MODEL ========" ) # Used BK with constant short-rate volatility sigma = 0.20 a = 0.01 model = BDTTree(sigma, num_time_steps) # print("BDT MODEL BERMUDAN SWAPTION CLASS BERMUDAN EXERCISE") valuePay = bermudan_swaption_pay.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN BDT PAY VALUE:", valuePay) valueRec = bermudan_swaption_rec.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN BDT REC VALUE:", valueRec) payRec = valuePay - valueRec testCases.print("PAY MINUS RECEIVER :", payRec) ########################################################################### ########################################################################### ########################################################################### # BDT MODEL ########################################################################### ########################################################################### ########################################################################### testCases.banner("=======================================================") testCases.banner("=======================================================") testCases.banner("======================= HW MODEL ======================") testCases.banner("=======================================================") testCases.banner("=======================================================") testCases.banner("====== 0% VOLATILITY EUROPEAN SWAPTION HW MODEL ======") sigma = 0.0000001 a = 0.1 num_time_steps = 200 model = HWTree(sigma, a, num_time_steps) valuePay = europeanSwaptionPay.value(valuation_date, libor_curve, model) testCases.print("EUROPEAN HW PAY VALUE:", valuePay) valueRec = europeanSwaptionRec.value(valuation_date, libor_curve, model) testCases.print("EUROPEAN HW REC VALUE:", valueRec) payRec = valuePay - valueRec testCases.print("PAY MINUS RECEIVER :", payRec) testCases.banner("===== 20% VOLATILITY EUROPEAN SWAPTION BDT MODEL ======") # Used BK with constant short-rate volatility sigma = 0.01 a = 0.01 model = HWTree(sigma, a, num_time_steps) testCases.banner("HW MODEL SWAPTION CLASS EUROPEAN EXERCISE") valuePay = europeanSwaptionPay.value(valuation_date, libor_curve, model) testCases.print("EUROPEAN HW PAY VALUE:", valuePay) valueRec = europeanSwaptionRec.value(valuation_date, libor_curve, model) testCases.print("EUROPEAN HW REC VALUE:", valueRec) payRec = valuePay - valueRec testCases.print("PAY MINUS RECEIVER :", payRec) ########################################################################### # Now we create the Bermudan swaptions but only allow European exercise fixed_leg_type = SwapTypes.PAY exercise_type = FinExerciseTypes.EUROPEAN bermudan_swaption_pay = IborBermudanSwaption( settlement_date, exercise_date, swapMaturityDate, fixed_leg_type, exercise_type, swapFixedCoupon, swapFixedFrequencyType, swapFixedDayCountType) fixed_leg_type = SwapTypes.RECEIVE bermudan_swaption_rec = IborBermudanSwaption( settlement_date, exercise_date, swapMaturityDate, fixed_leg_type, exercise_type, swapFixedCoupon, swapFixedFrequencyType, swapFixedDayCountType) testCases.banner( "======= 0% VOLATILITY BERMUDAN SWAPTION EUROPEAN EXERCISE HW MODEL ========" ) sigma = 0.000001 model = HWTree(sigma, a, num_time_steps) testCases.banner("BK MODEL BERMUDAN SWAPTION CLASS EUROPEAN EXERCISE") valuePay = bermudan_swaption_pay.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN BDT PAY VALUE:", valuePay) valueRec = bermudan_swaption_rec.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN BDT REC VALUE:", valueRec) payRec = valuePay - valueRec testCases.print("PAY MINUS RECEIVER :", payRec) testCases.banner( "======= 100bp VOLATILITY BERMUDAN SWAPTION EUROPEAN EXERCISE HW MODEL ========" ) # Used BK with constant short-rate volatility sigma = 0.01 model = HWTree(sigma, a, num_time_steps) testCases.banner("BDT MODEL BERMUDAN SWAPTION CLASS EUROPEAN EXERCISE") valuePay = bermudan_swaption_pay.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN BDT PAY VALUE:", valuePay) valueRec = bermudan_swaption_rec.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN BDT REC VALUE:", valueRec) payRec = valuePay - valueRec testCases.print("PAY MINUS RECEIVER :", payRec) ########################################################################### # Now we create the Bermudan swaptions but allow Bermudan exercise ########################################################################### fixed_leg_type = SwapTypes.PAY exercise_type = FinExerciseTypes.BERMUDAN bermudan_swaption_pay = IborBermudanSwaption( settlement_date, exercise_date, swapMaturityDate, fixed_leg_type, exercise_type, swapFixedCoupon, swapFixedFrequencyType, swapFixedDayCountType) fixed_leg_type = SwapTypes.RECEIVE bermudan_swaption_rec = IborBermudanSwaption( settlement_date, exercise_date, swapMaturityDate, fixed_leg_type, exercise_type, swapFixedCoupon, swapFixedFrequencyType, swapFixedDayCountType) testCases.banner( "======= ZERO VOLATILITY BERMUDAN SWAPTION BERMUDAN EXERCISE HW MODEL ========" ) # Used BK with constant short-rate volatility sigma = 0.000001 a = 0.01 model = HWTree(sigma, a, num_time_steps) testCases.banner("HW MODEL BERMUDAN SWAPTION CLASS BERMUDAN EXERCISE") valuePay = bermudan_swaption_pay.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN HW PAY VALUE:", valuePay) valueRec = bermudan_swaption_rec.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN HW REC VALUE:", valueRec) payRec = valuePay - valueRec testCases.print("PAY MINUS RECEIVER :", payRec) testCases.banner( "======= 100bps VOLATILITY BERMUDAN SWAPTION BERMUDAN EXERCISE HW MODEL ========" ) # Used BK with constant short-rate volatility sigma = 0.01 a = 0.01 model = HWTree(sigma, a, num_time_steps) testCases.banner("HW MODEL BERMUDAN SWAPTION CLASS BERMUDAN EXERCISE") valuePay = bermudan_swaption_pay.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN HW PAY VALUE:", valuePay) valueRec = bermudan_swaption_rec.value(valuation_date, libor_curve, model) testCases.print("BERMUDAN HW REC VALUE:", valueRec) payRec = valuePay - valueRec testCases.print("PAY MINUS RECEIVER :", payRec)
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)
valuation_date = Date(1, 1, 2011) exercise_date = Date(1, 1, 2012) swapMaturityDate = Date(1, 1, 2017) swapFixedFrequencyType = FrequencyTypes.SEMI_ANNUAL swapFixedDayCountType = DayCountTypes.ACT_365F model1 = Black(0.00001) model2 = BlackShifted(0.00001, 0.0) model3 = SABR(0.013, 0.5, 0.5, 0.5) model4 = SABRShifted(0.013, 0.5, 0.5, 0.5, -0.008) model5 = HWTree(0.00001, 0.00001) model6 = BKTree(0.01, 0.01) settlement_date = valuation_date.add_weekdays(2) libor_curve = build_curve(valuation_date) def test_pay(): libor_curve = build_curve(valuation_date) swaptionType = SwapTypes.PAY k = 0.02 swaption = IborSwaption(settlement_date, exercise_date, swapMaturityDate, swaptionType, k, swapFixedFrequencyType, swapFixedDayCountType)
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_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_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)