def test_FinInterpolatedForwards(): import matplotlib.pyplot as plt tValues = np.array([0.0, 3.0, 5.0, 10.0]) rValues = np.array([0.04, 0.07, 0.08, 0.09]) dfValues = np.exp(-tValues*rValues) tInterpValues = np.linspace(0.0, 12.0, 200) print(tValues) print(rValues) print(dfValues) curveDate = FinDate(2019,1,3) for method in FinInterpMethods: discountCurve = FinDiscountCurve(curveDate, tValues, dfValues, method) dfInterpValues = discountCurve.df(tInterpValues) fwdInterpValues = discountCurve.fwd(tInterpValues) zeroInterpValues = discountCurve.zeroRate(tInterpValues) if PLOT_GRAPHS: plt.figure(figsize=(8, 6)) plt.plot(tValues, dfValues, 'o', color='g', label="DFS:") plt.plot(tInterpValues, dfInterpValues, color='r', label="DF:" + str(method)) plt.legend() plt.figure(figsize=(8, 6)) plt.plot(tInterpValues, fwdInterpValues, color='r', label="FWD:" + str(method)) plt.plot(tInterpValues, zeroInterpValues, color='b', label="ZERO:" + str(method)) plt.plot(tValues, rValues, 'o', color='g', label="ZERO RATES") plt.legend()
def test_FinDiscountCurve(): startDate = FinDate(2018, 1, 1) times = np.linspace(0, 10.0, 10) rate = 0.05 values = np.exp(-rate * times) curve = FinDiscountCurve(startDate, times, values, FinInterpMethods.FLAT_FORWARDS) testCases.header("T", "DF") for t in np.linspace(0, 10, 21): df = curve.df(t) testCases.print(t, df)
def test_CDSFastApproximation(): valueDate = FinDate(2018, 6, 20) # I build a discount curve that requires no bootstrap times = np.linspace(0, 10.0, 11) r = 0.05 discountFactors = np.power((1.0 + r), -times) dates = valueDate.addYears(times) liborCurve = FinDiscountCurve(valueDate, dates, discountFactors, FinInterpTypes.FLAT_FWD_RATES) ########################################################################## maturityDate = valueDate.nextCDSDate(120) t = (maturityDate - valueDate) / 365.242 z = liborCurve.df(maturityDate) r = -np.log(z) / t recoveryRate = 0.40 contractCoupon = 0.010 testCases.header("MKT_SPD", "EXACT_VALUE", "APPROX_VALUE", "DIFF(%NOT)") for mktCoupon in np.linspace(0.000, 0.05, 21): cdsContracts = [] cdsMkt = FinCDS(valueDate, maturityDate, mktCoupon, ONE_MILLION) cdsContracts.append(cdsMkt) issuerCurve = FinCDSCurve(valueDate, cdsContracts, liborCurve, recoveryRate) cdsContract = FinCDS(valueDate, maturityDate, contractCoupon) v_exact = cdsContract.value( valueDate, issuerCurve, recoveryRate)['full_pv'] v_approx = cdsContract.valueFastApprox( valueDate, r, mktCoupon, recoveryRate)[0] pctdiff = (v_exact - v_approx) / ONE_MILLION * 100.0 testCases.print(mktCoupon * 10000, v_exact, v_approx, pctdiff)
def test_BlackKarasinskiExampleTwo(): # 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 settlementDate = FinDate(1, 12, 2019) expiryDate = settlementDate.addTenor("18m") maturityDate = settlementDate.addTenor("10Y") coupon = 0.05 frequencyType = FinFrequencyTypes.SEMI_ANNUAL accrualType = FinDayCountTypes.ACT_ACT_ICMA bond = FinBond(maturityDate, coupon, frequencyType, accrualType) bond.calculateFlowDates(settlementDate) couponTimes = [] couponFlows = [] cpn = bond._coupon / bond._frequency for flowDate in bond._flowDates: flowTime = (flowDate - settlementDate) / gDaysInYear couponTimes.append(flowTime) couponFlows.append(cpn) couponTimes = np.array(couponTimes) couponFlows = np.array(couponFlows) strikePrice = 105.0 face = 100.0 tmat = (maturityDate - settlementDate) / gDaysInYear texp = (expiryDate - settlementDate) / gDaysInYear times = np.linspace(0, tmat, 20) dfs = np.exp(-0.05 * times) curve = FinDiscountCurve(settlementDate, times, dfs) price = bond.fullPriceFromDiscountCurve(settlementDate, curve) print("Fixed Income Price:", price) sigma = 0.20 a = 0.05 # Test convergence numStepsList = [100] #,101,200,300,400,500,600,700,800,900,1000] isAmerican = True treeVector = [] for numTimeSteps in numStepsList: start = time.time() model = FinModelRatesBlackKarasinski(a, sigma) model.buildTree(tmat, int(numTimeSteps), times, dfs) v = model.bondOption(texp, strikePrice, face, couponTimes, couponFlows, isAmerican) end = time.time() period = end - start treeVector.append(v[0]) print(numTimeSteps, v, period)
def test_IssuerCurveBuild(): ''' Test issuer curve build with simple libor curve to isolate cds curve building time cost. ''' valuationDate = FinDate(20, 6, 2018) times = np.linspace(0.0, 10.0, 11) r = 0.05 discountFactors = np.power((1.0 + r), -times) dates = valuationDate.addYears(times) liborCurve = FinDiscountCurve(valuationDate, dates, discountFactors, FinInterpTypes.FLAT_FWD_RATES) recoveryRate = 0.40 cdsContracts = [] cdsCoupon = 0.005 # 50 bps maturityDate = valuationDate.addMonths(12) cds = FinCDS(valuationDate, maturityDate, cdsCoupon) cdsContracts.append(cds) cdsCoupon = 0.0055 maturityDate = valuationDate.addMonths(24) cds = FinCDS(valuationDate, maturityDate, cdsCoupon) cdsContracts.append(cds) cdsCoupon = 0.0060 maturityDate = valuationDate.addMonths(36) cds = FinCDS(valuationDate, maturityDate, cdsCoupon) cdsContracts.append(cds) cdsCoupon = 0.0065 maturityDate = valuationDate.addMonths(60) cds = FinCDS(valuationDate, maturityDate, cdsCoupon) cdsContracts.append(cds) cdsCoupon = 0.0070 maturityDate = valuationDate.addMonths(84) cds = FinCDS(valuationDate, maturityDate, cdsCoupon) cdsContracts.append(cds) cdsCoupon = 0.0073 maturityDate = valuationDate.addMonths(120) cds = FinCDS(valuationDate, maturityDate, cdsCoupon) cdsContracts.append(cds) issuerCurve = FinCDSCurve(valuationDate, cdsContracts, liborCurve, recoveryRate) return cdsContracts, issuerCurve
def test_FinInterpolatedForwards(): import matplotlib.pyplot as plt tValues = np.array([0.0, 3.0, 5.0, 10.0]) rValues = np.array([0.04, 0.07, 0.08, 0.09]) dfValues = np.exp(-tValues * rValues) tInterpValues = np.linspace(0.0, 12.0, 49) curveDate = FinDate(1, 1, 2019) tDates = curveDate.addYears(tValues) tInterpDates = curveDate.addYears(tInterpValues) for interpType in FinInterpTypes: discountCurve = FinDiscountCurve(curveDate, tDates, dfValues, interpType) dfInterpValues = discountCurve.df(tInterpDates) fwdInterpValues = discountCurve.fwd(tInterpDates) zeroInterpValues = discountCurve.zeroRate(tInterpDates) if PLOT_GRAPHS: plt.figure(figsize=(8, 6)) plt.plot(tValues, dfValues, 'o', color='g', label="DFS:") plt.plot(tInterpValues, dfInterpValues, color='r', label="DF:" + str(interpType)) plt.legend() plt.figure(figsize=(8, 6)) plt.plot(tInterpValues, fwdInterpValues, color='r', label="FWD:" + str(interpType)) plt.plot(tInterpValues, zeroInterpValues, color='b', label="ZERO:" + str(interpType)) plt.plot(tValues, rValues, 'o', color='g', label="ZERO RATES") plt.legend()
def test_dp_example(): # http://www.derivativepricing.com/blogpage.asp?id=8 startDate = FinDate(14, 11, 2011) endDate = FinDate(14, 11, 2016) fixedFreqType = FinFrequencyTypes.SEMI_ANNUAL swapCalendarType = FinCalendarTypes.TARGET busDayAdjustType = FinBusDayAdjustTypes.MODIFIED_FOLLOWING dateGenRuleType = FinDateGenRuleTypes.BACKWARD fixedDayCountType = FinDayCountTypes.THIRTY_E_360_ISDA swapType = FinLiborSwapTypes.PAYER fixedCoupon = 0.0124 notional = ONE_MILLION swap = FinLiborSwap(startDate, endDate, swapType, fixedCoupon=fixedCoupon, fixedFreqType=fixedFreqType, fixedDayCountType=fixedDayCountType, floatFreqType=FinFrequencyTypes.SEMI_ANNUAL, floatDayCountType=FinDayCountTypes.ACT_360, notional=notional, calendarType=swapCalendarType, busDayAdjustType=busDayAdjustType, dateGenRuleType=dateGenRuleType) # swap.printFixedLegFlows() dts = [FinDate(14, 11, 2011), FinDate(14, 5, 2012), FinDate(14, 11, 2012), FinDate(14, 5, 2013), FinDate(14, 11, 2013), FinDate(14, 5, 2014), FinDate(14, 11, 2014), FinDate(14, 5, 2015), FinDate(16, 11, 2015), FinDate(16, 5, 2016), FinDate(14, 11, 2016)] dfs = [0.9999843, 0.9966889, 0.9942107, 0.9911884, 0.9880738, 0.9836490, 0.9786276, 0.9710461, 0.9621778, 0.9514315, 0.9394919] valuationDate = startDate curve = FinDiscountCurve(valuationDate, dts, np.array(dfs), FinInterpTypes.FLAT_FORWARDS) v = swap.value(valuationDate, curve, curve) # swap.printFixedLegPV() # swap.printFloatLegPV() # This is essentially zero testCases.header("LABEL", "VALUE") testCases.print("Swap Value on a Notional of $1M:", v)
def test_CurveBuild(): valuationDate = FinDate(2018, 6, 20) times = np.linspace(0.0, 10.0, 10) r = 0.05 discountFactors = np.power((1.0 + r), -times) liborCurve = FinDiscountCurve(valuationDate, times, discountFactors, FinInterpMethods.FLAT_FORWARDS) recoveryRate = 0.40 cdsContracts = [] cdsCoupon = 0.005 # 50 bps maturityDate = valuationDate.addMonths(12) cds = FinCDS(valuationDate, maturityDate, cdsCoupon) cdsContracts.append(cds) cdsCoupon = 0.0055 maturityDate = valuationDate.addMonths(24) cds = FinCDS(valuationDate, maturityDate, cdsCoupon) cdsContracts.append(cds) cdsCoupon = 0.0060 maturityDate = valuationDate.addMonths(36) cds = FinCDS(valuationDate, maturityDate, cdsCoupon) cdsContracts.append(cds) cdsCoupon = 0.0065 maturityDate = valuationDate.addMonths(60) cds = FinCDS(valuationDate, maturityDate, cdsCoupon) cdsContracts.append(cds) cdsCoupon = 0.0070 maturityDate = valuationDate.addMonths(84) cds = FinCDS(valuationDate, maturityDate, cdsCoupon) cdsContracts.append(cds) cdsCoupon = 0.0073 maturityDate = valuationDate.addMonths(120) cds = FinCDS(valuationDate, maturityDate, cdsCoupon) cdsContracts.append(cds) issuerCurve = FinCDSCurve(valuationDate, cdsContracts, liborCurve, recoveryRate) return cdsContracts, issuerCurve
def test_FinBondOption(): settlementDate = FinDate(1, 12, 2019) maturityDate = settlementDate.addTenor("10Y") coupon = 0.05 frequencyType = FinFrequencyTypes.SEMI_ANNUAL accrualType = FinDayCountTypes.ACT_ACT_ICMA bond = FinBond(maturityDate, coupon, frequencyType, accrualType) tmat = (maturityDate - settlementDate) / gDaysInYear times = np.linspace(0, tmat, 20) dfs = np.exp(-0.05 * times) discountCurve = FinDiscountCurve(settlementDate, times, dfs) expiryDate = settlementDate.addTenor("18m") strikePrice = 105.0 face = 100.0 optionType = FinBondOptionTypes.AMERICAN_CALL price = bond.fullPriceFromDiscountCurve(settlementDate, discountCurve) print("Fixed Income Price:", price) for strikePrice in [90, 95, 100, 105, 110]: sigma = 0.01 a = 0.1 bondOption = FinBondOption(bond, expiryDate, strikePrice, face, optionType) model = FinModelRatesHullWhite(a, sigma) v = bondOption.value(settlementDate, discountCurve, model) print("HW", strikePrice, v) for strikePrice in [90, 95, 100, 105, 110]: sigma = 0.20 a = 0.05 bondOption = FinBondOption(bond, expiryDate, strikePrice, face, optionType) model = FinModelRatesBlackKarasinski(a, sigma) v = bondOption.value(settlementDate, discountCurve, model) print("BK", strikePrice, v)
def test_BlackKarasinskiExampleOne(): # HULL BOOK INITIAL EXAMPLE SECTION 28.7 HW EDITION 6 times = [0.0, 0.5000, 1.00000, 1.50000, 2.00000, 2.500000, 3.00000] zeros = [0.03, 0.0343, 0.03824, 0.04183, 0.04512, 0.048512, 0.05086] times = np.array(times) zeros = np.array(zeros) dfs = np.exp(-zeros * times) startDate = FinDate(1, 12, 2019) curve = FinDiscountCurve(startDate, times, dfs) endDate = FinDate(1, 12, 2022) sigma = 0.25 a = 0.22 numTimeSteps = 6 tmat = (endDate - startDate) / gDaysInYear model = FinModelRatesBlackKarasinski(a, sigma) model.buildTree(tmat, numTimeSteps, times, dfs) printTree(model._Q) print("") printTree(model._rt) print("")
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() settlementDate = FinDate(1, 1, 2020) times = np.array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0]) dates = settlementDate.addYears(times) rate = 0.06 dfs = 1.0 / (1.0 + rate / 2.0)**(2.0 * times) curve = FinDiscountCurve(settlementDate, dates, dfs) coupon = 0.06 freqType = FinFrequencyTypes.SEMI_ANNUAL accrualType = FinDayCountTypes.ACT_ACT_ICMA strikePrice = 100.0 face = 100.0 # Andersen paper numTimeSteps = 200 testCases.header("ExerciseType", "Sigma", "NumSteps", "Texp", "Tmat", "V_Fixed", "V_pay", "V_rec") for exerciseType in [FinExerciseTypes.EUROPEAN, FinExerciseTypes.BERMUDAN]: for maturityYears in [4.0, 5.0, 10.0, 20.0]: maturityDate = settlementDate.addYears(maturityYears) issueDate = FinDate(maturityDate._d, maturityDate._m, 2000) if maturityYears == 4.0 or maturityYears == 5.0: sigma = 0.2012 elif maturityYears == 10.0: sigma = 0.1522 elif maturityYears == 20.0: sigma = 0.1035 for expiryYears in range( int(maturityYears / 2) - 1, int(maturityYears)): expiryDate = settlementDate.addYears(expiryYears) tmat = (maturityDate - settlementDate) / gDaysInYear texp = (expiryDate - settlementDate) / gDaysInYear bond = FinBond(issueDate, maturityDate, coupon, freqType, accrualType) couponTimes = [] couponFlows = [] cpn = bond._coupon / bond._frequency for flowDate in bond._flowDates: if flowDate > expiryDate: flowTime = (flowDate - settlementDate) / gDaysInYear couponTimes.append(flowTime) couponFlows.append(cpn) couponTimes = np.array(couponTimes) couponFlows = np.array(couponFlows) price = bond.cleanPriceFromDiscountCurve(settlementDate, curve) model = FinModelRatesBDT(sigma, numTimeSteps) model.buildTree(tmat, times, dfs) v = model.bermudanSwaption(texp, tmat, strikePrice, face, couponTimes, couponFlows, exerciseType) testCases.print("%s" % exerciseType, "%9.5f" % sigma, "%9.5f" % numTimeSteps, "%9.5f" % expiryYears, "%9.5f" % maturityYears, "%9.5f" % price, "%9.2f" % (v['pay'] * 100.0), "%9.2f" % (v['rec'] * 100.0))
def test_BDTExampleTwo(): # Valuation of a European option on a coupon bearing bond # This follows example in Fig 28.11 of John Hull's book (6th Edition) # but does not have the exact same dt so there are some differences testCases.banner("===================== FIG 28.11 HULL BOOK =============") settlementDate = FinDate(1, 12, 2019) issueDate = FinDate(1, 12, 2015) expiryDate = settlementDate.addTenor("18m") maturityDate = settlementDate.addTenor("10Y") coupon = 0.05 freqType = FinFrequencyTypes.SEMI_ANNUAL accrualType = FinDayCountTypes.ACT_ACT_ICMA bond = FinBond(issueDate, maturityDate, coupon, freqType, accrualType) couponTimes = [] couponFlows = [] cpn = bond._coupon / bond._frequency numFlows = len(bond._flowDates) for i in range(1, numFlows): pcd = bond._flowDates[i - 1] ncd = bond._flowDates[i] if pcd < settlementDate and ncd > settlementDate: flowTime = (pcd - settlementDate) / gDaysInYear couponTimes.append(flowTime) couponFlows.append(cpn) for flowDate in bond._flowDates: if flowDate > settlementDate: flowTime = (flowDate - settlementDate) / gDaysInYear couponTimes.append(flowTime) couponFlows.append(cpn) couponTimes = np.array(couponTimes) couponFlows = np.array(couponFlows) strikePrice = 105.0 face = 100.0 tmat = (maturityDate - settlementDate) / gDaysInYear texp = (expiryDate - settlementDate) / gDaysInYear times = np.linspace(0, tmat, 11) dates = settlementDate.addYears(times) dfs = np.exp(-0.05 * times) testCases.header("LABEL", "VALUES") testCases.print("TIMES:", times) curve = FinDiscountCurve(settlementDate, dates, dfs) price = bond.cleanPriceFromDiscountCurve(settlementDate, curve) testCases.print("Fixed Income Price:", price) sigma = 0.20 # Test convergence numStepsList = [5] #[100, 200, 300, 400, 500, 600, 700, 800, 900, 1000] exerciseType = FinExerciseTypes.AMERICAN testCases.header("Values") treeVector = [] for numTimeSteps in numStepsList: model = FinModelRatesBDT(sigma, numTimeSteps) model.buildTree(tmat, times, dfs) v = model.bondOption(texp, strikePrice, face, couponTimes, couponFlows, exerciseType) testCases.print(v) treeVector.append(v['call']) if PLOT_GRAPHS: plt.plot(numStepsList, treeVector) # The value in Hull converges to 0.699 with 100 time steps while I get 0.70 if 1 == 0: print("RT") printTree(model._rt, 5) print("Q") printTree(model._Q, 5)
def test_FinBondOption(): settlementDate = FinDate(1, 12, 2019) issueDate = FinDate(1, 12, 2018) maturityDate = settlementDate.addTenor("10Y") coupon = 0.05 freqType = FinFrequencyTypes.SEMI_ANNUAL accrualType = FinDayCountTypes.ACT_ACT_ICMA bond = FinBond(issueDate, maturityDate, coupon, freqType, accrualType) tmat = (maturityDate - settlementDate) / gDaysInYear times = np.linspace(0, tmat, 20) dates = settlementDate.addYears(times) dfs = np.exp(-0.05 * times) discountCurve = FinDiscountCurve(settlementDate, dates, dfs) expiryDate = settlementDate.addTenor("18m") strikePrice = 105.0 face = 100.0 ########################################################################### strikes = [80, 90, 100, 110, 120] optionType = FinOptionTypes.EUROPEAN_CALL testCases.header("LABEL", "VALUE") price = bond.fullPriceFromDiscountCurve(settlementDate, discountCurve) testCases.print("Fixed Income Price:", price) numTimeSteps = 100 testCases.header("OPTION TYPE AND MODEL", "STRIKE", "VALUE") for strikePrice in strikes: sigma = 0.20 bondOption = FinBondOption(bond, expiryDate, strikePrice, face, optionType) model = FinModelRatesBDT(sigma, numTimeSteps) v = bondOption.value(settlementDate, discountCurve, model) testCases.print("EUROPEAN CALL - BK", strikePrice, v) for strikePrice in strikes: sigma = 0.20 bondOption = FinBondOption(bond, expiryDate, strikePrice, face, optionType) model = FinModelRatesBDT(sigma, numTimeSteps) v = bondOption.value(settlementDate, discountCurve, model) testCases.print("EUROPEAN CALL - BK", strikePrice, v) ########################################################################### optionType = FinOptionTypes.AMERICAN_CALL price = bond.fullPriceFromDiscountCurve(settlementDate, discountCurve) testCases.header("LABEL", "VALUE") testCases.print("Fixed Income Price:", price) testCases.header("OPTION TYPE AND MODEL", "STRIKE", "VALUE") for strikePrice in strikes: sigma = 0.20 bondOption = FinBondOption(bond, expiryDate, strikePrice, face, optionType) model = FinModelRatesBDT(sigma, numTimeSteps) v = bondOption.value(settlementDate, discountCurve, model) testCases.print("AMERICAN CALL - BK", strikePrice, v) for strikePrice in strikes: sigma = 0.20 bondOption = FinBondOption(bond, expiryDate, strikePrice, face, optionType) model = FinModelRatesBDT(sigma, numTimeSteps) v = bondOption.value(settlementDate, discountCurve, model) testCases.print("AMERICAN CALL - BK", strikePrice, v) ########################################################################### optionType = FinOptionTypes.EUROPEAN_PUT price = bond.fullPriceFromDiscountCurve(settlementDate, discountCurve) for strikePrice in strikes: sigma = 0.01 bondOption = FinBondOption(bond, expiryDate, strikePrice, face, optionType) model = FinModelRatesBDT(sigma, numTimeSteps) v = bondOption.value(settlementDate, discountCurve, model) testCases.print("EUROPEAN PUT - BK", strikePrice, v) for strikePrice in strikes: sigma = 0.20 bondOption = FinBondOption(bond, expiryDate, strikePrice, face, optionType) model = FinModelRatesBDT(sigma, numTimeSteps) v = bondOption.value(settlementDate, discountCurve, model) testCases.print("EUROPEAN PUT - BK", strikePrice, v) ########################################################################### optionType = FinOptionTypes.AMERICAN_PUT price = bond.fullPriceFromDiscountCurve(settlementDate, discountCurve) for strikePrice in strikes: sigma = 0.02 bondOption = FinBondOption(bond, expiryDate, strikePrice, face, optionType) model = FinModelRatesBDT(sigma, numTimeSteps) v = bondOption.value(settlementDate, discountCurve, model) testCases.print("AMERICAN PUT - BK", strikePrice, v) for strikePrice in strikes: sigma = 0.20 bondOption = FinBondOption(bond, expiryDate, strikePrice, face, optionType) model = FinModelRatesBDT(sigma, numTimeSteps) v = bondOption.value(settlementDate, discountCurve, model) testCases.print("AMERICAN PUT - BK", strikePrice, v)
def test_FinDiscountCurve(): # Create a curve from times and discount factors startDate = FinDate(1, 1, 2018) years = np.linspace(0, 10, 6) rate = 0.05 + 0.005*years - 0.0003*years*years dfs = np.exp(-rate * years) dates = startDate.addYears(years) curve = FinDiscountCurve(startDate, dates, dfs, FinInterpTypes.FLAT_FORWARDS) testCases.header("T", "DF", "ZERORATE", "CC_FWD", "MM_FWD", "SURVPROB") plotYears = np.linspace(0, 12, 12*12+1) plotDates = startDate.addYears(plotYears) # Examine dependency of curve on compounding rate zeroRates_A = curve.zeroRate(plotDates, FinFrequencyTypes.ANNUAL) zeroRates_S = curve.zeroRate(plotDates, FinFrequencyTypes.SEMI_ANNUAL) zeroRates_Q = curve.zeroRate(plotDates, FinFrequencyTypes.QUARTERLY) zeroRates_M = curve.zeroRate(plotDates, FinFrequencyTypes.MONTHLY) zeroRates_C = curve.zeroRate(plotDates, FinFrequencyTypes.CONTINUOUS) if PLOT_GRAPHS: plt.figure(figsize=(6, 4)) plt.plot(plotYears, scale(zeroRates_A, 100), label='A') plt.plot(plotYears, scale(zeroRates_S, 100), label='S') plt.plot(plotYears, scale(zeroRates_Q, 100), label='Q') plt.plot(plotYears, scale(zeroRates_M, 100), label='M') plt.plot(plotYears, scale(zeroRates_C, 100), label='C') plt.ylim((5, 8)) plt.title('Discount Curves') plt.xlabel('Time (years)') plt.ylabel('Zero Rate (%)') plt.legend(loc='lower right', frameon=False) # Examine dependency of fwd curve on the interpolation scheme for interp in FinInterpTypes: curve = FinDiscountCurve(startDate, dates, dfs, interp) fwdRates = curve.fwd(plotDates) zeroRates = curve.zeroRate(plotDates, FinFrequencyTypes.ANNUAL) parRates = curve.swapRate(startDate, plotDates, FinFrequencyTypes.ANNUAL) if PLOT_GRAPHS: plt.figure(figsize=(6, 4)) plt.plot(plotYears, scale(fwdRates, 100), label='FWD RATES') plt.plot(plotYears, scale(zeroRates, 100), label='ZERO RATES') plt.plot(plotYears, scale(parRates, 100), label='PAR RATES') plt.ylim((3.0, 8.5)) plt.title('Forward Curves using ' + str(interp)) plt.xlabel('Time (years)') plt.ylabel('Fwd Rate (%)') plt.legend(loc='lower right', frameon=False)
def test_HullWhiteCallableBond(): # Valuation of a European option on a coupon bearing bond settlementDate = FinDate(1, 12, 2019) maturityDate = settlementDate.addTenor("10Y") coupon = 0.05 frequencyType = FinFrequencyTypes.SEMI_ANNUAL accrualType = FinDayCountTypes.ACT_ACT_ICMA bond = FinBond(maturityDate, coupon, frequencyType, accrualType) bond.calculateFlowDates(settlementDate) couponTimes = [] couponFlows = [] cpn = bond._coupon / bond._frequency for flowDate in bond._flowDates[1:]: flowTime = (flowDate - settlementDate) / gDaysInYear couponTimes.append(flowTime) couponFlows.append(cpn) couponTimes = np.array(couponTimes) couponFlows = np.array(couponFlows) ########################################################################### # Set up the call and put times and prices ########################################################################### callDates = [] callPrices = [] callPx = 120.0 callDates.append(settlementDate.addTenor("5Y")) callPrices.append(callPx) callDates.append(settlementDate.addTenor("6Y")) callPrices.append(callPx) callDates.append(settlementDate.addTenor("7Y")) callPrices.append(callPx) callDates.append(settlementDate.addTenor("8Y")) callPrices.append(callPx) callTimes = [] for dt in callDates: t = (dt - settlementDate) / gDaysInYear callTimes.append(t) putDates = [] putPrices = [] putPx = 98.0 putDates.append(settlementDate.addTenor("5Y")) putPrices.append(putPx) putDates.append(settlementDate.addTenor("6Y")) putPrices.append(putPx) putDates.append(settlementDate.addTenor("7Y")) putPrices.append(putPx) putDates.append(settlementDate.addTenor("8Y")) putPrices.append(putPx) putTimes = [] for dt in putDates: t = (dt - settlementDate) / gDaysInYear putTimes.append(t) ########################################################################### tmat = (maturityDate - settlementDate) / gDaysInYear times = np.linspace(0, tmat, 20) dfs = np.exp(-0.05 * times) curve = FinDiscountCurve(settlementDate, times, dfs) ########################################################################### v1 = bond.fullPriceFromDiscountCurve(settlementDate, curve) sigma = 0.02 # basis point volatility a = 0.1 # Test convergence numStepsList = [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000] tmat = (maturityDate - settlementDate) / gDaysInYear print("NUMSTEPS", "BOND_ONLY", "CALLABLE_BOND", "TIME") for numTimeSteps in numStepsList: start = time.time() model = FinModelRatesHullWhite(a, sigma) model.buildTree(tmat, numTimeSteps, times, dfs) v2 = model.callablePuttableBond_Tree(couponTimes, couponFlows, callTimes, callPrices, putTimes, putPrices) end = time.time() period = end - start print(numTimeSteps, v1, v2, period) if 1 == 0: print("RT") printTree(model._rt, 5) print("BOND") printTree(model._bondValues, 5) print("OPTION") printTree(model._optionValues, 5)
def test_FinBondOption(): settlementDate = FinDate(1, 12, 2019) issueDate = FinDate(1, 12, 2018) maturityDate = settlementDate.addTenor("10Y") coupon = 0.05 freqType = FinFrequencyTypes.SEMI_ANNUAL accrualType = FinDayCountTypes.ACT_ACT_ICMA bond = FinBond(issueDate, maturityDate, coupon, freqType, accrualType) times = np.linspace(0, 10.0, 21) dfs = np.exp(-0.05 * times) dates = settlementDate.addYears(times) discountCurve = FinDiscountCurve(settlementDate, dates, dfs) expiryDate = settlementDate.addTenor("18m") strikePrice = 105.0 face = 100.0 ########################################################################### strikes = [80, 85, 90, 95, 100, 105, 110, 115, 120] optionType = FinOptionTypes.EUROPEAN_CALL testCases.header("LABEL", "VALUE") price = bond.cleanPriceFromDiscountCurve(settlementDate, discountCurve) testCases.print("Fixed Income Price:", price) numTimeSteps = 100 testCases.banner("HW EUROPEAN CALL") testCases.header("STRIKE", "VALUE") for strikePrice in strikes: sigma = 0.01 a = 0.1 bondOption = FinBondOption(bond, expiryDate, strikePrice, face, optionType) model = FinModelRatesHW(sigma, a, numTimeSteps) v = bondOption.value(settlementDate, discountCurve, model) testCases.print(strikePrice, v) ########################################################################### optionType = FinOptionTypes.AMERICAN_CALL price = bond.cleanPriceFromDiscountCurve(settlementDate, discountCurve) testCases.header("LABEL", "VALUE") testCases.print("Fixed Income Price:", price) testCases.banner("HW AMERICAN CALL") testCases.header("STRIKE", "VALUE") for strikePrice in strikes: sigma = 0.01 a = 0.1 bondOption = FinBondOption(bond, expiryDate, strikePrice, face, optionType) model = FinModelRatesHW(sigma, a) v = bondOption.value(settlementDate, discountCurve, model) testCases.print(strikePrice, v) ########################################################################### optionType = FinOptionTypes.EUROPEAN_PUT testCases.banner("HW EUROPEAN PUT") testCases.header("STRIKE", "VALUE") price = bond.cleanPriceFromDiscountCurve(settlementDate, discountCurve) for strikePrice in strikes: sigma = 0.01 a = 0.1 bondOption = FinBondOption(bond, expiryDate, strikePrice, face, optionType) model = FinModelRatesHW(sigma, a) v = bondOption.value(settlementDate, discountCurve, model) testCases.print(strikePrice, v) ########################################################################### optionType = FinOptionTypes.AMERICAN_PUT testCases.banner("HW AMERICAN PUT") testCases.header("STRIKE", "VALUE") price = bond.cleanPriceFromDiscountCurve(settlementDate, discountCurve) for strikePrice in strikes: sigma = 0.02 a = 0.1 bondOption = FinBondOption(bond, expiryDate, strikePrice, face, optionType) model = FinModelRatesHW(sigma, a) v = bondOption.value(settlementDate, discountCurve, model) testCases.print(strikePrice, v)
def test_FinDiscountCurves(): # Create a curve from times and discount factors valuationDate = FinDate(1, 1, 2018) years = [1.0, 2.0, 3.0, 4.0, 5.0] dates = valuationDate.addYears(years) years2 = [] for dt in dates: y = (dt - valuationDate) / gDaysInYear years2.append(y) rates = np.array([0.05, 0.06, 0.065, 0.07, 0.075]) discountFactors = np.exp(-np.array(rates) * np.array(years2)) curvesList = [] finDiscountCurve = FinDiscountCurve(valuationDate, dates, discountFactors, FinInterpTypes.FLAT_FORWARDS) curvesList.append(finDiscountCurve) finDiscountCurveFlat = FinDiscountCurveFlat(valuationDate, 0.05) curvesList.append(finDiscountCurveFlat) finDiscountCurveNS = FinDiscountCurveNS(valuationDate, 0.0305, -0.01, 0.08, 10.0) curvesList.append(finDiscountCurveNS) finDiscountCurveNSS = FinDiscountCurveNSS(valuationDate, 0.035, -0.02, 0.09, 0.1, 1.0, 2.0) curvesList.append(finDiscountCurveNSS) finDiscountCurvePoly = FinDiscountCurvePoly(valuationDate, [0.05, 0.002, -0.00005]) curvesList.append(finDiscountCurvePoly) finDiscountCurvePWF = FinDiscountCurvePWF(valuationDate, dates, rates) curvesList.append(finDiscountCurvePWF) finDiscountCurvePWL = FinDiscountCurvePWL(valuationDate, dates, rates) curvesList.append(finDiscountCurvePWL) finDiscountCurveZeros = FinDiscountCurveZeros(valuationDate, dates, rates) curvesList.append(finDiscountCurveZeros) curveNames = [] for curve in curvesList: curveNames.append(type(curve).__name__) testCases.banner("SINGLE CALLS NO VECTORS") testCases.header("CURVE", "DATE", "ZERO", "DF", "CCFWD", "MMFWD", "SWAP") years = np.linspace(1, 10, 10) fwdMaturityDates = valuationDate.addYears(years) testCases.banner("######################################################") testCases.banner("SINGLE CALLS") testCases.banner("######################################################") for name, curve in zip(curveNames, curvesList): for fwdMaturityDate in fwdMaturityDates: tenor = "3M" zeroRate = curve.zeroRate(fwdMaturityDate) fwd = curve.fwd(fwdMaturityDate) fwdRate = curve.fwdRate(fwdMaturityDate, tenor) swapRate = curve.swapRate(valuationDate, fwdMaturityDate) df = curve.df(fwdMaturityDate) testCases.print("%-20s" % name, "%-12s" % fwdMaturityDate, "%7.6f" % (zeroRate), "%8.7f" % (df), "%7.6f" % (fwd), "%7.6f" % (fwdRate), "%7.6f" % (swapRate)) # Examine vectorisation testCases.banner("######################################################") testCases.banner("VECTORISATIONS") testCases.banner("######################################################") for name, curve in zip(curveNames, curvesList): tenor = "3M" zeroRate = curve.zeroRate(fwdMaturityDates) fwd = curve.fwd(fwdMaturityDates) fwdRate = curve.fwdRate(fwdMaturityDates, tenor) swapRate = curve.swapRate(valuationDate, fwdMaturityDates) df = curve.df(fwdMaturityDates) for i in range(0, len(fwdMaturityDates)): testCases.print("%-20s" % name, "%-12s" % fwdMaturityDate, "%7.6f" % (zeroRate[i]), "%8.7f" % (df[i]), "%7.6f" % (fwd[i]), "%7.6f" % (fwdRate[i]), "%7.6f" % (swapRate[i])) if PLOT_GRAPHS: years = np.linspace(0, 10, 121) years2 = years + 1.0 fwdDates = valuationDate.addYears(years) fwdDates2 = valuationDate.addYears(years2) plt.figure() for name, curve in zip(curveNames, curvesList): zeroRates = curve.zeroRate(fwdDates) plt.plot(years, zeroRates, label=name) plt.legend() plt.title("Zero Rates") plt.figure() for name, curve in zip(curveNames, curvesList): fwdRates = curve.fwd(fwdDates) plt.plot(years, fwdRates, label=name) plt.legend() plt.title("CC Fwd Rates") plt.figure() for name, curve in zip(curveNames, curvesList): fwdRates = curve.fwdRate(fwdDates, fwdDates2) plt.plot(years, fwdRates, label=name) plt.legend() plt.title("CC Fwd Rates") plt.figure() for name, curve in zip(curveNames, curvesList): dfs = curve.df(fwdDates) plt.plot(years, dfs, label=name) plt.legend() plt.title("Discount Factors")
def test_HullWhiteBondOption(): # Valuation of a European option on a coupon bearing bond settlementDate = FinDate(1, 12, 2019) expiryDate = settlementDate.addTenor("18m") maturityDate = settlementDate.addTenor("10Y") coupon = 0.05 frequencyType = FinFrequencyTypes.SEMI_ANNUAL accrualType = FinDayCountTypes.ACT_ACT_ICMA bond = FinBond(maturityDate, coupon, frequencyType, accrualType) bond.calculateFlowDates(settlementDate) couponTimes = [] couponFlows = [] cpn = bond._coupon / bond._frequency for flowDate in bond._flowDates[1:]: flowTime = (flowDate - settlementDate) / gDaysInYear couponTimes.append(flowTime) couponFlows.append(cpn) couponTimes = np.array(couponTimes) couponFlows = np.array(couponFlows) strikePrice = 105.0 face = 100.0 tmat = (maturityDate - settlementDate) / gDaysInYear times = np.linspace(0, tmat, 20) dfs = np.exp(-0.05 * times) curve = FinDiscountCurve(settlementDate, times, dfs) price = bond.fullPriceFromDiscountCurve(settlementDate, curve) print("Spot Bond Price:", price) price = bond.fullPriceFromDiscountCurve(expiryDate, curve) print("Fwd Bond Price:", price) sigma = 0.01 a = 0.1 # Test convergence numStepsList = [100, 200, 300, 400, 500] texp = (expiryDate - settlementDate) / gDaysInYear print("NUMSTEPS", "FAST TREE", "FULLTREE", "TIME") for numTimeSteps in numStepsList: start = time.time() model = FinModelRatesHullWhite(a, sigma) model.buildTree(texp, numTimeSteps, times, dfs) americanExercise = False v1 = model.americanBondOption_Tree(texp, strikePrice, face, couponTimes, couponFlows, americanExercise) v2 = model.europeanBondOption_Tree(texp, strikePrice, face, couponTimes, couponFlows) end = time.time() period = end - start print(numTimeSteps, v1, v2, period) # plt.plot(numStepsList, treeVector) if 1 == 0: print("RT") printTree(model._rt, 5) print("BOND") printTree(model._bondValues, 5) print("OPTION") printTree(model._optionValues, 5) v = model.europeanBondOption_Jamshidian(texp, strikePrice, face, couponTimes, couponFlows, times, dfs) print("EUROPEAN BOND JAMSHIDIAN DECOMP", v)