def test_FinDateAddYears(): startDate = FinDate(1, 1, 2010) testCases.header("Years", "Dates") years = [1, 3, 5, 7, 10] dates1 = startDate.addYears(years) for dt in dates1: testCases.print("DATES1", dt) years = np.array([1, 3, 5, 7, 10]) dates2 = startDate.addYears(years) for dt in dates2: testCases.print("DATES2", dt) years = np.array([1.5, 3.25, 5.75, 7.25, 10.0]) dates3 = startDate.addYears(years) for dt in dates3: testCases.print("DATES3", dt) dt = 1.0 / 365.0 years = np.array( [1.5 + 2.0 * dt, 3.5 - 6 * dt, 5.75 + 3 * dt, 7.25 + dt, 10.0 + dt]) dates4 = startDate.addYears(years) for dt in dates4: testCases.print("DATES4", dt)
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_FWD_RATES) testCases.header("T", "DF", "ZERORATE", "CC_FWD", "MM_FWD", "SURVPROB") plotYears = np.linspace(0, 12, 12 * 12 + 1)[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_FinDiscountCurveZeros(): startDate = FinDate(1, 1, 2018) times = np.linspace(1.0, 10.0, 10) dates = startDate.addYears(times) zeroRates = np.linspace(5.0, 6.0, 10) / 100 freqType = FinFrequencyTypes.ANNUAL dayCountType = FinDayCountTypes.ACT_ACT_ISDA curve = FinDiscountCurveZeros(startDate, dates, zeroRates, freqType, dayCountType, FinInterpTypes.FLAT_FWD_RATES) testCases.header("T", "DF") years = np.linspace(0, 10, 21) dates = startDate.addYears(years) for dt in dates: df = curve.df(dt) testCases.print(dt, df) # print(curve) ############################################################################### numRepeats = 100 start = time.time() for i in range(0, numRepeats): freqType = FinFrequencyTypes.ANNUAL dayCountType = FinDayCountTypes.ACT_ACT_ISDA dates = [ FinDate(14, 6, 2016), FinDate(14, 9, 2016), FinDate(14, 12, 2016), FinDate(14, 6, 2017), FinDate(14, 6, 2019), FinDate(14, 6, 2021), FinDate(15, 6, 2026), FinDate(16, 6, 2031), FinDate(16, 6, 2036), FinDate(14, 6, 2046) ] zeroRates = [ 0.000000, 0.006616, 0.007049, 0.007795, 0.009599, 0.011203, 0.015068, 0.017583, 0.018998, 0.020080 ] startDate = dates[0] curve = FinDiscountCurveZeros(startDate, dates, zeroRates, freqType, dayCountType, FinInterpTypes.FLAT_FWD_RATES) end = time.time() period = end - start
def test_BDTExampleOne(): # HULL BOOK NOTES # http://www-2.rotman.utoronto.ca/~hull/technicalnotes/TechnicalNote23.pdf valuationDate = FinDate(1, 1, 2020) years = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0] zeroDates = valuationDate.addYears(years) zeroRates = [0.00, 0.10, 0.11, 0.12, 0.125, 0.13] testCases.header("DATES") testCases.print(zeroDates) testCases.header("RATES") testCases.print(zeroRates) curve = FinDiscountCurveZeros(valuationDate, zeroDates, zeroRates, FinFrequencyTypes.ANNUAL) yieldVol = 0.16 numTimeSteps = 5 tmat = years[-1] dfs = curve.df(zeroDates) testCases.print("DFS") testCases.print(dfs) years = np.array(years) dfs = np.array(dfs) model = FinModelRatesBDT(yieldVol, numTimeSteps) model.buildTree(tmat, years, dfs)
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_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_FinDiscountCurvePolynomial(): times = np.linspace(0.00, 10.0, 21) curveDate = FinDate(2019, 2, 2) dates = curveDate.addYears(times) coeffs = [0.0004, -0.0001, 0.00000010] curve1 = FinDiscountCurvePoly(curveDate, coeffs) zeros = curve1.zeroRate(dates) fwds = curve1.fwd(dates) if PLOT_GRAPHS: import matplotlib.pyplot as plt plt.figure(figsize=(6, 4)) plt.plot(times, zeros, label="Zeros") plt.plot(times, fwds, label="Forwards") plt.xlabel('Time (years)') plt.ylabel('Zero Rate') plt.legend(loc='best')
def test_FinNelsonSiegelCurve(): tau = 2.0 times = np.linspace(0.0, 10.0, 5) curveDate = FinDate(2019, 6, 6) dates = curveDate.addYears(times) curve1 = FinDiscountCurveNS(curveDate, 1, 0, 0, tau) factor1loading = curve1.zeroRate(dates) curve2 = FinDiscountCurveNS(curveDate, 0, 1, 0, tau) factor2loading = curve2.zeroRate(dates) curve3 = FinDiscountCurveNS(curveDate, 0, 0, 1, tau) factor3loading = curve3.zeroRate(dates) testCases.header("FACTOR LOADING ON ZERO RATES") testCases.print(factor1loading) testCases.print(factor2loading) testCases.print(factor3loading) if PLOT_GRAPHS: plt.figure(figsize=(6, 4)) plt.plot(times, scale(factor1loading, 1), label='beta1') plt.plot(times, scale(factor2loading, 1), label='beta2') plt.plot(times, scale(factor3loading, 1), label='beta3') plt.ylim((0, 1.05)) plt.title('Factor Loadings in Nelson-Siegel Model') plt.xlabel('Time (years)') plt.ylabel('Loading') plt.legend(loc='best') ########################################################################### testCases.header("BETA1", "BETA2", "BETA3", "ZEROS") beta1 = 0.03 beta2 = -0.02 beta3 = 0.02 curve1 = FinDiscountCurveNS(curveDate, beta1, beta2, beta3, tau) zeroRates1 = curve1.zeroRate(dates) testCases.print(beta1, beta2, beta3, zeroRates1) beta1 = 0.04 beta2 = -0.02 beta3 = 0.02 curve2 = FinDiscountCurveNS(curveDate, beta1, beta2, beta3, tau) zeroRates2 = curve2.zeroRate(dates) testCases.print(beta1, beta2, beta3, zeroRates2) beta1 = 0.05 beta2 = -0.02 beta3 = 0.02 curve3 = FinDiscountCurveNS(curveDate, beta1, beta2, beta3, tau) zeroRates3 = curve3.zeroRate(dates) testCases.print(beta1, beta2, beta3, zeroRates3) beta1 = 0.06 beta2 = -0.02 beta3 = 0.02 curve4 = FinDiscountCurveNS(curveDate, beta1, beta2, beta3, tau) zeroRates4 = curve4.zeroRate(dates) testCases.print(beta1, beta2, beta3, zeroRates4) beta1 = 0.07 beta2 = -0.02 beta3 = 0.02 curve5 = FinDiscountCurveNS(curveDate, beta1, beta2, beta3, tau) zeroRates5 = curve5.zeroRate(dates) testCases.print(beta1, beta2, beta3, zeroRates5) if PLOT_GRAPHS: plt.figure(figsize=(6, 4)) plt.plot(times, scale(zeroRates1, 100), label='beta1=3%') plt.plot(times, scale(zeroRates2, 100), label='beta1=4%') plt.plot(times, scale(zeroRates3, 100), label='beta1=5%') plt.plot(times, scale(zeroRates4, 100), label='beta1=6%') plt.plot(times, scale(zeroRates5, 100), label='beta1=7%') plt.ylim((0, 7.5)) plt.title('Nelson-Siegel Zero Rate Curves') plt.xlabel('Time (years)') plt.ylabel('Zero Rate (%)') plt.legend(loc='lower right', frameon=False) ########################################################################### beta1 = 0.06 beta2 = -0.04 beta3 = 0.02 curve1 = FinDiscountCurveNS(curveDate, beta1, beta2, beta3, tau) zeroRates1 = curve1.zeroRate(dates) testCases.print(beta1, beta2, beta3, zeroRates1) beta1 = 0.06 beta2 = -0.02 beta3 = 0.02 curve2 = FinDiscountCurveNS(curveDate, beta1, beta2, beta3, tau) zeroRates2 = curve2.zeroRate(dates) testCases.print(beta1, beta2, beta3, zeroRates2) beta1 = 0.06 beta2 = 0.00 beta3 = 0.02 curve3 = FinDiscountCurveNS(curveDate, beta1, beta2, beta3, tau) zeroRates3 = curve3.zeroRate(dates) testCases.print(beta1, beta2, beta3, zeroRates3) beta1 = 0.06 beta2 = 0.02 beta3 = 0.02 curve4 = FinDiscountCurveNS(curveDate, beta1, beta2, beta3, tau) zeroRates4 = curve4.zeroRate(dates) testCases.print(beta1, beta2, beta3, zeroRates4) beta1 = 0.06 beta2 = 0.04 beta3 = 0.02 curve5 = FinDiscountCurveNS(curveDate, beta1, beta2, beta3, tau) zeroRates5 = curve5.zeroRate(dates) testCases.print(beta1, beta2, beta3, zeroRates5) if PLOT_GRAPHS: plt.figure(figsize=(6, 4)) plt.plot(times, scale(zeroRates1, 100), label='beta2=-4%') plt.plot(times, scale(zeroRates2, 100), label='beta2=-2%') plt.plot(times, scale(zeroRates3, 100), label='beta2=0%') plt.plot(times, scale(zeroRates4, 100), label='beta2=2%') plt.plot(times, scale(zeroRates5, 100), label='beta2=4%') plt.ylim((0, 10)) plt.title('Nelson-Siegel Zero Rate Curves: Varying beta2') plt.xlabel('Time (years)') plt.ylabel('Zero Rate (%)') plt.legend(loc='lower right', frameon=False) beta1 = 0.06 beta2 = -0.02 beta3 = -0.02 curve1 = FinDiscountCurveNS(curveDate, beta1, beta2, beta3, tau) zeroRates1 = curve1.zeroRate(dates) testCases.print(beta1, beta2, beta3, zeroRates1) beta1 = 0.06 beta2 = -0.02 beta3 = 0.00 curve2 = FinDiscountCurveNS(curveDate, beta1, beta2, beta3, tau) zeroRates2 = curve2.zeroRate(dates) testCases.print(beta1, beta2, beta3, zeroRates2) beta1 = 0.06 beta2 = -0.02 beta3 = 0.02 curve3 = FinDiscountCurveNS(curveDate, beta1, beta2, beta3, tau) zeroRates3 = curve3.zeroRate(dates) testCases.print(beta1, beta2, beta3, zeroRates3) beta1 = 0.06 beta2 = -0.02 beta3 = 0.04 curve4 = FinDiscountCurveNS(curveDate, beta1, beta2, beta3, tau) zeroRates4 = curve4.zeroRate(dates) testCases.print(beta1, beta2, beta3, zeroRates4) beta1 = 0.06 beta2 = -0.02 beta3 = 0.06 curve5 = FinDiscountCurveNS(curveDate, beta1, beta2, beta3, tau) zeroRates5 = curve5.zeroRate(dates) testCases.print(beta1, beta2, beta3, zeroRates5) if PLOT_GRAPHS: plt.figure(figsize=(6, 4)) plt.plot(times, scale(zeroRates1, 100), label='beta3=-2%') plt.plot(times, scale(zeroRates2, 100), label='beta3=0%') plt.plot(times, scale(zeroRates3, 100), label='beta3=2%') plt.plot(times, scale(zeroRates4, 100), label='beta3=4%') plt.plot(times, scale(zeroRates5, 100), label='beta3=6%') plt.ylim((3.5, 7.5)) plt.title('Nelson-Siegel Zero Rate Curves: Varying beta3') plt.xlabel('Time (years)') plt.ylabel('Zero Rate (%)') plt.legend(loc='lower right', frameon=False)
def test_FinIborDepositsFuturesSwaps(): spotDate = FinDate(6, 6, 2018) spotDays = 0 settlementDate = spotDate.addWeekDays(spotDays) depoDCCType = FinDayCountTypes.ACT_360 depos = [] depositRate = 0.0231381 depo = FinIborDeposit(settlementDate, "3M", depositRate, depoDCCType) depos.append(depo) depositRate = 0.027 depo = FinIborDeposit(settlementDate, "3M", depositRate, depoDCCType) depos.append(depo) depos = [] depo = FinIborDeposit(settlementDate, "1M", 0.0230, depoDCCType) depos.append(depo) depo = FinIborDeposit(settlementDate, "2M", 0.0235, depoDCCType) depos.append(depo) depo = FinIborDeposit(settlementDate, "3M", 0.0240, depoDCCType) depos.append(depo) fras = [] fraRate = futureToFRARate(97.6675, -0.00005) fraSettlementDate = spotDate.nextIMMDate() fraMaturityDate = fraSettlementDate.nextIMMDate() fra = FinIborFRA(fraSettlementDate, fraMaturityDate, fraRate, depoDCCType) fras.append(fra) fraRate = futureToFRARate(97.5200, -0.00060) fraSettlementDate = fraMaturityDate fraMaturityDate = fraSettlementDate.nextIMMDate() fra = FinIborFRA(fraSettlementDate, fraMaturityDate, fraRate, depoDCCType) fras.append(fra) fraRate = futureToFRARate(97.3550, -0.00146) fraSettlementDate = fraMaturityDate fraMaturityDate = fraSettlementDate.nextIMMDate() fra = FinIborFRA(fraSettlementDate, fraMaturityDate, fraRate, depoDCCType) fras.append(fra) fraRate = futureToFRARate(97.2450, -0.00263) fraSettlementDate = fraMaturityDate fraMaturityDate = fraSettlementDate.nextIMMDate() fra = FinIborFRA(fraSettlementDate, fraMaturityDate, fraRate, depoDCCType) fras.append(fra) fraRate = futureToFRARate(97.1450, -0.00411) fraSettlementDate = fraMaturityDate fraMaturityDate = fraSettlementDate.nextIMMDate() fra = FinIborFRA(fraSettlementDate, fraMaturityDate, fraRate, depoDCCType) fras.append(fra) fraRate = futureToFRARate(97.0750, -0.00589) fraSettlementDate = fraSettlementDate.nextIMMDate() fraMaturityDate = fraSettlementDate.nextIMMDate() fra = FinIborFRA(fraSettlementDate, fraMaturityDate, fraRate, depoDCCType) fras.append(fra) ########################################################################### spotDays = 2 startDate = spotDate.addWeekDays(spotDays) swaps = [] fixedLegType = FinSwapTypes.PAY fixedDCCType = FinDayCountTypes.THIRTY_E_360 fixedFreqType = FinFrequencyTypes.SEMI_ANNUAL floatFreqType = FinFrequencyTypes.QUARTERLY notional = 1000000 principal = 0.0 floatSpread = 0.0 floatDCCType = FinDayCountTypes.ACT_360 calendarType = FinCalendarTypes.US busDayAdjustRule = FinBusDayAdjustTypes.PRECEDING swapRate = 0.02776305 swap = FinIborSwapOLD(startDate, "2Y", fixedLegType, swapRate, fixedFreqType, fixedDCCType, notional, floatSpread, floatFreqType, floatDCCType, calendarType, busDayAdjustRule) swaps.append(swap) liborCurve = FinIborSingleCurveOLD(spotDate, depos, fras, swaps) times = np.linspace(0.0, 2.0, 25) dates = spotDate.addYears(times) zeroRates = liborCurve.zeroRate(dates) fwdRates = liborCurve.fwd(dates) if PLOT_GRAPHS: plt.figure(figsize=(8, 6)) plt.plot(times, zeroRates * 100, label="zero rates") plt.plot(times, fwdRates * 100, label="fwd rates") plt.xlabel("Times") plt.ylabel("CC forward rates") plt.legend() print("==============================================================") for fra in fras: print(fra) print("==============================================================") endDate = spotDate df = liborCurve.df(endDate) print(endDate, df) endDate = settlementDate df = liborCurve.df(endDate) print(endDate, df) endDate = FinDate(20, 6, 2018) df = liborCurve.df(endDate) print(endDate, df) for depo in depos: endDate = depo._maturityDate df = liborCurve.df(endDate) print(endDate, df) for fra in fras: endDate = fra._maturityDate df = liborCurve.df(endDate) print(endDate, df) for swap in swaps: endDate = swap._maturityDate df = liborCurve.df(endDate) print(endDate, df) swap.printFixedLegPV(spotDate) swap.printFloatLegPV(spotDate)
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_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_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_FinFXMktVolSurface3(verboseCalibration): ########################################################################### if 1 == 1: # Example from Book extract by Iain Clark using Tables 4.4 and 4.5 # where we examine the calibration to a full surface in Chapter 4 valueDate = FinDate(10, 4, 2020) forName = "EUR" domName = "USD" forCCRate = 0.03460 # EUR domCCRate = 0.02940 # USD domDiscountCurve = FinDiscountCurveFlat(valueDate, domCCRate) forDiscountCurve = FinDiscountCurveFlat(valueDate, forCCRate) currencyPair = forName + domName spotFXRate = 1.3465 tenors = ['1Y', '2Y'] atmVols = [18.250, 17.677] marketStrangle25DeltaVols = [0.95, 0.85] riskReversal25DeltaVols = [-0.60, -0.562] marketStrangle10DeltaVols = [3.806, 3.208] riskReversal10DeltaVols = [-1.359, -1.208] notionalCurrency = forName # I HAVE NO YET MADE DELTA METHOD A VECTOR FOR EACH TERM AS I WOULD # NEED TO DO AS DESCRIBED IN CLARK PAGE 70 atmMethod = FinFXATMMethod.FWD_DELTA_NEUTRAL deltaMethod = FinFXDeltaMethod.FORWARD_DELTA # THIS IS DIFFERENT volFunctionType = FinVolFunctionTypes.CLARK5 alpha = 0.5 # FIT WINGS AT 10D if ALPHA = 1.0 fxMarketPlus = FinFXVolSurfacePlus(valueDate, spotFXRate, currencyPair, notionalCurrency, domDiscountCurve, forDiscountCurve, tenors, atmVols, marketStrangle25DeltaVols, riskReversal25DeltaVols, marketStrangle10DeltaVols, riskReversal10DeltaVols, alpha, atmMethod, deltaMethod, volFunctionType) fxMarketPlus.checkCalibration(False) if 1==0: # PLOT_GRAPHS: fxMarketPlus.plotVolCurves() plt.figure() dbns = fxMarketPlus.impliedDbns(0.5, 2.0, 1000) for i in range(0, len(dbns)): plt.plot(dbns[i]._x, dbns[i]._densitydx) plt.title(volFunctionType) print("SUM:", dbns[i].sum()) # Test interpolation years = [1.0, 1.5, 2.0] dates = valueDate.addYears(years) strikes = np.linspace(1.0, 2.0, 20) if 1==0: volSurface = [] for k in strikes: volSmile = [] for dt in dates: vol = fxMarketPlus.volatilityFromStrikeDate(k, dt) volSmile.append(vol*100.0) print(k, dt, vol*100.0) volSurface.append(volSmile) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') X, Y = np.meshgrid(years, strikes) zs = np.array(volSurface) Z = zs.reshape(X.shape) ax.plot_surface(X, Y, Z) ax.set_xlabel('Years') ax.set_ylabel('Strikes') ax.set_zlabel('Volatility') plt.show() ####################################################################### deltas = np.linspace(0.10, 0.90, 17) if 1==0: volSurface = [] for delta in deltas: volSmile = [] for dt in dates: (vol, k) = fxMarketPlus.volatilityFromDeltaDate(delta, dt) volSmile.append(vol*100.0) print(delta, k, dt, vol*100.0) volSurface.append(volSmile) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') X, Y = np.meshgrid(years, deltas) zs = np.array(volSurface) Z = zs.reshape(X.shape) ax.plot_surface(X, Y, Z) ax.set_xlabel('Years') ax.set_ylabel('Delta') ax.set_zlabel('Volatility') plt.show()
def testImpliedVolatility_NEW(): valueDate = FinDate(1, 1, 2015) stockPrice = 100.0 interestRate = 0.05 dividendYield = 0.03 discountCurve = FinDiscountCurveFlat(valueDate, interestRate) dividendCurve = FinDiscountCurveFlat(valueDate, dividendYield) strikes = np.linspace(50, 150, 11) timesToExpiry = [0.003, 0.01, 0.1, 0.5, 1.0, 2.0, 5.0] sigmas = np.arange(1, 100, 5) / 100.0 optionTypes = [FinOptionTypes.EUROPEAN_CALL, FinOptionTypes.EUROPEAN_PUT] testCases.header("OPT_TYPE", "TEXP", "STOCK_PRICE", "STRIKE", "INTRINSIC", "VALUE", "INPUT_VOL", "IMPLIED_VOL") tol = 1e-5 numTests = 0 numFails = 0 for vol in sigmas: model = FinModelBlackScholes(vol) for timeToExpiry in timesToExpiry: expiryDate = valueDate.addYears(timeToExpiry) for strike in strikes: for optionType in optionTypes: option = FinEquityVanillaOption(expiryDate, strike, optionType) value = option.value(valueDate, stockPrice, discountCurve, dividendCurve, model) intrinsic = option.intrinsic(valueDate, stockPrice, discountCurve, dividendCurve) # I remove the cases where the time value is zero # This is arbitrary but 1e-10 seems good enough to me impliedVol = -999 if value - intrinsic > 1e-10: impliedVol = option.impliedVolatility(valueDate, stockPrice, discountCurve, dividendCurve, value) numTests += 1 errVol = np.abs(impliedVol - vol) if errVol > tol: testCases.print(optionType, timeToExpiry, stockPrice, strike, intrinsic, value, vol, impliedVol) # These fails include ones due to the zero time value numFails += 1 testCases.print(optionType, timeToExpiry, stockPrice, strike, stockPrice, value, vol, impliedVol) assert numFails == 694, "Num Fails has changed."
def test_FinFXMktVolSurface4(verboseCalibration): ########################################################################### # Here I remove the 25D Vols ########################################################################### if 1 == 1: # Example from Book extract by Iain Clark using Tables 3.3 and 3.4 # print("EURUSD EXAMPLE CLARK") valueDate = FinDate(10, 4, 2020) forName = "EUR" domName = "USD" forCCRate = 0.03460 # EUR domCCRate = 0.02940 # USD domDiscountCurve = FinDiscountCurveFlat(valueDate, domCCRate) forDiscountCurve = FinDiscountCurveFlat(valueDate, forCCRate) currencyPair = forName + domName spotFXRate = 1.3465 tenors = ['1M', '2M', '3M', '6M', '1Y', '2Y'] atmVols = [21.00, 21.00, 20.750, 19.400, 18.250, 17.677] marketStrangle25DeltaVols = [0.65, 0.75, 0.85, 0.90, 0.95, 0.85] riskReversal25DeltaVols = [-0.20, -0.25, -0.30, -0.50, -0.60, -0.562] marketStrangle10DeltaVols = [2.433, 2.83, 3.228, 3.485, 3.806, 3.208] riskReversal10DeltaVols = [ -1.258, -1.297, -1.332, -1.408, -1.359, -1.208 ] marketStrangle25DeltaVols = None riskReversal25DeltaVols = None notionalCurrency = forName atmMethod = FinFXATMMethod.FWD_DELTA_NEUTRAL deltaMethod = FinFXDeltaMethod.SPOT_DELTA volFunctionType = FinVolFunctionTypes.CLARK alpha = 0.50 # FIT WINGS AT 10D if ALPHA = 1.0 fxMarketPlus = FinFXVolSurfacePlus( valueDate, spotFXRate, currencyPair, notionalCurrency, domDiscountCurve, forDiscountCurve, tenors, atmVols, marketStrangle25DeltaVols, riskReversal25DeltaVols, marketStrangle10DeltaVols, riskReversal10DeltaVols, alpha, atmMethod, deltaMethod, volFunctionType) fxMarketPlus.checkCalibration(False) years = [1.0 / 12.0, 2. / 12., 0.25, 0.5, 1.0, 2.0] dates = valueDate.addYears(years) deltas = np.linspace(0.10, 0.90, 17) if 1 == 1: volSurface = [] for delta in deltas: volSmile = [] for dt in dates: (vol, k) = fxMarketPlus.volatilityFromDeltaDate(delta, dt) volSmile.append(vol * 100.0) print(delta, k, dt, vol * 100.0) volSurface.append(volSmile) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') X, Y = np.meshgrid(years, deltas) zs = np.array(volSurface) Z = zs.reshape(X.shape) ax.plot_surface(X, Y, Z) ax.set_xlabel('Years') ax.set_ylabel('Delta') ax.set_zlabel('Volatility') plt.title("EURUSD Volatility Surface") plt.show()
def test_FinNelsonSiegelSvenssonCurve(): tau1 = 2.0 tau2 = 0.5 times = np.linspace(0.0, 10.0, 5) startDate = FinDate(1, 1, 2020) dates = startDate.addYears(times) curve1 = FinDiscountCurveNSS(startDate, 1., 0., 0., 0., tau1, tau2) factor1loading = curve1.zeroRate(dates) curve2 = FinDiscountCurveNSS(startDate, 0., 1., 0., 0., tau1, tau2) factor2loading = curve2.zeroRate(dates) curve3 = FinDiscountCurveNSS(startDate, 0., 0., 1., 0., tau1, tau2) factor3loading = curve3.zeroRate(dates) curve4 = FinDiscountCurveNSS(startDate, 0., 0., 0., 1., tau1, tau2) factor4loading = curve4.zeroRate(dates) testCases.header("FACTOR LOADING ON ZERO RATES") testCases.print(factor1loading) testCases.print(factor2loading) testCases.print(factor3loading) testCases.print(factor4loading) # plt.figure(figsize = (6,4)) # plt.plot(times,scaleVector(factor1loading,1),label='beta1'); # plt.plot(times,scaleVector(factor2loading,1),label='beta2'); # plt.plot(times,scaleVector(factor3loading,1),label='beta3'); # plt.ylim((0,1.05)) # # plt.title('Factor Loadings in Nelson-Siegel Model'); # plt.xlabel('Time (years)'); # plt.ylabel('Loading'); # plt.legend(loc='best') ########################################################################## testCases.header("BETA1", "BETA2", "BETA3", "BETA4", "ZEROS") beta1 = 0.03 beta2 = -0.02 beta3 = -0.02 beta4 = 0.08 curve1 = FinDiscountCurveNSS(startDate, beta1, beta2, beta3, beta4, tau1, tau2) zeroRates1 = curve1.zeroRate(dates) testCases.print(beta1, beta2, beta3, beta4, zeroRates1) beta1 = 0.04 beta2 = -0.02 beta3 = -0.02 beta4 = 0.08 curve2 = FinDiscountCurveNSS(startDate, beta1, beta2, beta3, beta4, tau1, tau2) zeroRates2 = curve2.zeroRate(dates) testCases.print(beta1, beta2, beta3, beta4, zeroRates2) beta1 = 0.05 beta2 = -0.02 beta3 = -0.02 beta4 = 0.08 curve3 = FinDiscountCurveNSS(startDate, beta1, beta2, beta3, beta4, tau1, tau2) zeroRates3 = curve3.zeroRate(dates) testCases.print(beta1, beta2, beta3, beta4, zeroRates3) beta1 = 0.06 beta2 = -0.02 beta3 = -0.02 beta4 = 0.08 curve4 = FinDiscountCurveNSS(startDate, beta1, beta2, beta3, beta4, tau1, tau2) zeroRates4 = curve4.zeroRate(dates) testCases.print(beta1, beta2, beta3, beta4, zeroRates4) beta1 = 0.07 beta2 = -0.02 beta3 = -0.02 beta4 = 0.08 curve5 = FinDiscountCurveNSS(startDate, beta1, beta2, beta3, beta4, tau1, tau2) zeroRates5 = curve5.zeroRate(dates) testCases.print(beta1, beta2, beta3, beta4, zeroRates5) if PLOT_GRAPHS: plt.figure(figsize=(6, 4)) plt.plot(times, scale(zeroRates1, 100), label='beta1=3%') plt.plot(times, scale(zeroRates2, 100), label='beta1=4%') plt.plot(times, scale(zeroRates3, 100), label='beta1=5%') plt.plot(times, scale(zeroRates4, 100), label='beta1=6%') plt.plot(times, scale(zeroRates5, 100), label='beta1=7%') plt.ylim((0, 7.5)) plt.title('Nelson-Siegel Zero Rate Curves') plt.xlabel('Time (years)') plt.ylabel('Zero Rate (%)') plt.legend(loc='lower right', frameon=False)
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 buildFullIssuerCurve2(mktSpreadBump, irBump): # https://www.markit.com/markit.jsp?jsppage=pv.jsp # YIELD CURVE 20 August 2020 SNAP AT 1600 m = 1.0 settlementDate = FinDate(24, 8, 2020) dcType = FinDayCountTypes.ACT_360 depos = [] maturityDate = settlementDate.addMonths(1) depo1 = FinLiborDeposit(settlementDate, maturityDate, m * 0.001709, dcType) maturityDate = settlementDate.addMonths(2) depo2 = FinLiborDeposit(settlementDate, maturityDate, m * 0.002123, dcType) maturityDate = settlementDate.addMonths(3) depo3 = FinLiborDeposit(settlementDate, maturityDate, m * 0.002469, dcType) maturityDate = settlementDate.addMonths(6) depo4 = FinLiborDeposit(settlementDate, maturityDate, m * 0.003045, dcType) maturityDate = settlementDate.addMonths(12) depo5 = FinLiborDeposit(settlementDate, maturityDate, m * 0.004449, dcType) depos.append(depo1) depos.append(depo2) depos.append(depo3) depos.append(depo4) depos.append(depo5) swaps = [] dcType = FinDayCountTypes.THIRTY_E_360_ISDA fixedFreq = FinFrequencyTypes.SEMI_ANNUAL maturityDate = settlementDate.addMonths(24) swap1 = FinLiborSwap(settlementDate, maturityDate, FinSwapTypes.PAYER, m * 0.002155 + irBump, fixedFreq, dcType) swaps.append(swap1) maturityDate = settlementDate.addMonths(36) swap2 = FinLiborSwap(settlementDate, maturityDate, FinSwapTypes.PAYER, m * 0.002305 + irBump, fixedFreq, dcType) swaps.append(swap2) maturityDate = settlementDate.addMonths(48) swap3 = FinLiborSwap(settlementDate, maturityDate, FinSwapTypes.PAYER, m * 0.002665 + irBump, fixedFreq, dcType) swaps.append(swap3) maturityDate = settlementDate.addMonths(60) swap4 = FinLiborSwap(settlementDate, maturityDate, FinSwapTypes.PAYER, m * 0.003290 + irBump, fixedFreq, dcType) swaps.append(swap4) liborCurve = FinLiborCurve(settlementDate, depos, [], swaps) cdsCoupon = 0.01 + mktSpreadBump cdsMarketContracts = [] effectiveDate = FinDate(21, 8, 2020) cds = FinCDS(effectiveDate, "6M", cdsCoupon) cdsMarketContracts.append(cds) cds = FinCDS(effectiveDate, "1Y", cdsCoupon) cdsMarketContracts.append(cds) cds = FinCDS(effectiveDate, "2Y", cdsCoupon) cdsMarketContracts.append(cds) cds = FinCDS(effectiveDate, "3Y", cdsCoupon) cdsMarketContracts.append(cds) cds = FinCDS(effectiveDate, "4Y", cdsCoupon) cdsMarketContracts.append(cds) cds = FinCDS(effectiveDate, "5Y", cdsCoupon) cdsMarketContracts.append(cds) cds = FinCDS(effectiveDate, "7Y", cdsCoupon) cdsMarketContracts.append(cds) cds = FinCDS(effectiveDate, "10Y", cdsCoupon) cdsMarketContracts.append(cds) recoveryRate = 0.40 issuerCurve = FinCDSCurve(settlementDate, cdsMarketContracts, liborCurve, recoveryRate) testCases.header("DATE", "DISCOUNT_FACTOR", "SURV_PROB") years = np.linspace(0.0, 10.0, 20) dates = settlementDate.addYears(years) for dt in dates: df = liborCurve.df(dt) q = issuerCurve.survProb(dt) testCases.print("%16s" % dt, "%12.8f" % df, "%12.8f" % q) return liborCurve, issuerCurve
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 testImpliedVolatility(): valueDate = FinDate(1, 1, 2015) stockPrice = 100 interestRate = 0.05 dividendYield = 0.01 discountCurve = FinDiscountCurveFlat(valueDate, interestRate) strikes = [10, 20, 50, 100, 150, 200] timesToExpiry = [0.003, 0.01, 0.1, 0.5, 1.0, 2.0, 5.0] expiryDates = valueDate.addYears(timesToExpiry) sigmas = [0.01, 0.10, 0.50, 1.0] optionTypes = [FinOptionTypes.EUROPEAN_CALL, FinOptionTypes.EUROPEAN_PUT] testCases.header("OPT_TYPE", "EXP_DATE", "STRIKE", "STOCK_PRICE", "VALUE", "INPUT_VOL", "IMPLIED_VOL") tol = 1e-6 numTests = 0 numFails = 0 for vol in sigmas: model = FinModelBlackScholes(vol) for expiryDate in expiryDates: for strike in strikes: for optionType in optionTypes: option = FinEquityVanillaOption(expiryDate, 100.0, optionType) value = option.value(valueDate, stockPrice, discountCurve, dividendYield, model) impliedVol = option.impliedVolatility( valueDate, stockPrice, discountCurve, dividendYield, value) numTests += 1 if np.abs(impliedVol - vol) > tol: numFails += 1 # print(optionType, expiryDate, strike, # stockPrice, value, vol, impliedVol) testCases.print(optionType, expiryDate, strike, stockPrice, value, vol, impliedVol) print("Num Tests", numTests, "numFails", numFails) ############################################################################### K = [10, 20, 50, 100, 150, 200] T = [0.003, 0.01, 0.1, 0.5, 1.0, 2.0, 5.0] sigma = [0.01, 0.10, 0.50, 1.0] optionTypes = [FinOptionTypes.EUROPEAN_CALL, FinOptionTypes.EUROPEAN_PUT] stockPrice = 100 HOURS_PER_YEAR = 365.25 * 24 convergenceFailure = 0 assertionFailure = 0 noResult = 0 successful = 0 numberTests = 0 totalElapsedTime = 0. for t in T: expDate = valueDate.addHours(t * HOURS_PER_YEAR) for k in K: for vol in sigma: bs_model = FinModelBlackScholes(vol) for type_ in optionTypes: option = FinEquityVanillaOption(expDate, k, type_) value = option.value(valueDate, stockPrice, discountCurve, dividendYield, bs_model) if value < 1e-10: continue try: start_time = time.time() impliedVol = option.impliedVolatility_v2( valueDate, stockPrice, discountCurve, dividendYield, value) assert abs(impliedVol - vol) < 0.10 except FinError: noResult += 1 except AssertionError: assertionFailure += 1 print("-----------------") print( f"Did not converge to expected value: {round(impliedVol, 2)} vs {vol}" ) print("INPUTS\n", "Type:", type_.name, "ttm:", t, "strike:", k, "Expected IV:", vol, "BS Price:", value) except RuntimeError: import traceback traceback.print_exc() convergenceFailure += 1 print("INPUTS\n", "Type:", type_.name, "ttm:", t, "strike:", k, "Expected IV:", vol) except Exception: import traceback traceback.print_exc() noResult += 1 else: successful += 1 totalElapsedTime += time.time() - start_time finally: numberTests += 1 print("\nSuccessful:", successful) print("Convergence failure:", convergenceFailure) print("Inaccurate result:", assertionFailure) print("No result (price too low)", noResult) print("TOTAL:", numberTests) print("Mean time:", 1e6 * totalElapsedTime / successful, "us")