Skip to content

Commit

Permalink
Fixed pandas issue with append and lots of small bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
domokane committed Jul 31, 2024
1 parent ef94e99 commit d4bd48c
Show file tree
Hide file tree
Showing 17 changed files with 553 additions and 24 deletions.
2 changes: 1 addition & 1 deletion financepy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cr = "\n"

s = "####################################################################" + cr
s += "# FINANCEPY BETA Version " + str('0.360') + " - This build: 31 Jul 2024 at 19:44 #" + cr
s += "# FINANCEPY BETA Version " + str('0.360') + " - This build: 31 Jul 2024 at 23:01 #" + cr
s += "# This software is distributed FREE AND WITHOUT ANY WARRANTY #" + cr
s += "# Report bugs as issues at https://github.com/domokane/FinancePy #" + cr
s += "####################################################################"
Expand Down
2 changes: 1 addition & 1 deletion financepy/products/rates/ibor_benchmarks_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def benchmarks_report(benchmarks,
if df_bmi is None:
df_bmi = pd.DataFrame.from_dict(res, orient='index').T
else:
df_bmi = df_bmi.append(res, ignore_index=True)
df_bmi = df_bmi._append(res, ignore_index=True)

if include_objects:
df_bmi['benchmark_objects'] = benchmarks
Expand Down
2 changes: 1 addition & 1 deletion golden_tests/TestFinBondPortfolio.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
def test_BondPortfolio():

import pandas as pd
path = os.path.join(os.path.dirname(__file__), './data/giltBondPrices.txt')
path = os.path.join(os.path.dirname(__file__), './data/gilt_bond_prices.txt')
bondDataFrame = pd.read_csv(path, sep='\t')
bondDataFrame['mid'] = 0.5*(bondDataFrame['bid'] + bondDataFrame['ask'])

Expand Down
2 changes: 1 addition & 1 deletion golden_tests/TestFinBondYieldCurve.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def test_BondYieldCurve():
###########################################################################

import pandas as pd
path = os.path.join(os.path.dirname(__file__), './data/giltBondPrices.txt')
path = os.path.join(os.path.dirname(__file__), './data/gilt_bond_prices.txt')
bondDataFrame = pd.read_csv(path, sep='\t')
bondDataFrame['mid'] = 0.5*(bondDataFrame['bid'] + bondDataFrame['ask'])

Expand Down
2 changes: 1 addition & 1 deletion golden_tests/TestFinBondZeroCurve.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
def test_BondZeroCurve():

import pandas as pd
path = os.path.join(os.path.dirname(__file__), './data/giltBondPrices.txt')
path = os.path.join(os.path.dirname(__file__), './data/gilt_bond_prices.txt')
bondDataFrame = pd.read_csv(path, sep='\t')
bondDataFrame['mid'] = 0.5*(bondDataFrame['bid'] + bondDataFrame['ask'])

Expand Down
105 changes: 105 additions & 0 deletions golden_tests/TestFinIborBenchmarksReport.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import pandas as pd
from os.path import dirname, join

import sys
sys.path.append("..")


from financepy.utils.date import Date
from financepy.utils.math import ONE_MILLION
from financepy.utils.global_types import SwapTypes
from financepy.utils.frequency import FrequencyTypes
from financepy.utils.day_count import DayCountTypes
from financepy.utils.calendar import Calendar, CalendarTypes
from financepy.market.curves.interpolator import InterpTypes
from financepy.products.rates.ibor_swap import IborSwap
from financepy.products.rates.ibor_fra import IborFRA
from financepy.products.rates.ibor_deposit import IborDeposit
from financepy.products.rates.ibor_future import IborFuture
from financepy.products.rates.ibor_single_curve import IborSingleCurve

from financepy.products.rates.ibor_benchmarks_report import ibor_benchmarks_report, dataframe_to_benchmarks


def test_ibor_benchmarks_report():
valuation_date = Date(6, 10, 2001)
cal = CalendarTypes.UNITED_KINGDOM
interp_type = InterpTypes.FLAT_FWD_RATES

depoDCCType = DayCountTypes.ACT_360
depos = []
spot_days = 2
settlement_date = valuation_date.add_weekdays(spot_days)
depo = IborDeposit(settlement_date, "3M", 4.2/100.0, depoDCCType, cal_type=cal)
depos.append(depo)

fraDCCType = DayCountTypes.ACT_360
fras = []
fra = IborFRA(settlement_date.add_tenor("3M"), "3M", 4.20/100.0, fraDCCType, cal_type=cal)
fras.append(fra)

swaps = []
swapType = SwapTypes.PAY
fixedDCCType = DayCountTypes.THIRTY_E_360_ISDA
fixedFreqType = FrequencyTypes.SEMI_ANNUAL

swap = IborSwap(settlement_date, "1Y", swapType, 4.20/100.0, fixedFreqType, fixedDCCType, cal_type=cal)
swaps.append(swap)
swap = IborSwap(settlement_date, "2Y", swapType, 4.30/100.0, fixedFreqType, fixedDCCType, cal_type=cal)
swaps.append(swap)
swap = IborSwap(settlement_date, "3Y", swapType, 4.70/100.0, fixedFreqType, fixedDCCType, cal_type=cal)
swaps.append(swap)
swap = IborSwap(settlement_date, "5Y", swapType, 5.40/100.0, fixedFreqType, fixedDCCType, cal_type=cal)
swaps.append(swap)
swap = IborSwap(settlement_date, "7Y", swapType, 5.70/100.0, fixedFreqType, fixedDCCType, cal_type=cal)
swaps.append(swap)
swap = IborSwap(settlement_date, "10Y", swapType, 6.00/100.0, fixedFreqType, fixedDCCType, cal_type=cal)
swaps.append(swap)
swap = IborSwap(settlement_date, "12Y", swapType, 6.10/100.0, fixedFreqType, fixedDCCType, cal_type=cal)
swaps.append(swap)
swap = IborSwap(settlement_date, "15Y", swapType, 5.90/100.0, fixedFreqType, fixedDCCType, cal_type=cal)
swaps.append(swap)
swap = IborSwap(settlement_date, "20Y", swapType, 5.60/100.0, fixedFreqType, fixedDCCType, cal_type=cal)
swaps.append(swap)
swap = IborSwap(settlement_date, "25Y", swapType, 5.55/100.0, fixedFreqType, fixedDCCType, cal_type=cal)
swaps.append(swap)

# Create but do not build the initial curve
do_build = True
curve = IborSingleCurve(valuation_date, depos, fras, swaps,
interp_type, check_refit=False, do_build=do_build)

bechmarks_report = ibor_benchmarks_report(curve)

# print(bechmarks_report)

# Confirm that there are no NaNs. In particular this means that different types of benchmarks
# return exactly the same keys, just like we want it, with a couple of exceptions
assert (bechmarks_report
.drop(columns=['fixed_freq_type', 'fixed_leg_type'])
.isnull().values.any()
) == False


def test_dataframe_to_benchmarks():
path = dirname(__file__)
filename = "ibor_benchmarks_example.csv"
full_filename_path = join(path, "data", filename)

asof = Date(6, 10, 2001)

df = pd.read_csv(full_filename_path, index_col=0)
df['start_date'] = pd.to_datetime(df['start_date'], errors='ignore') # allow tenors
df['maturity_date'] = pd.to_datetime(df['maturity_date'], errors='ignore') # allow tenors

benchmarks = dataframe_to_benchmarks(
df, asof_date=asof, calendar_type=CalendarTypes.UNITED_KINGDOM)

assert len(benchmarks['IborDeposit']) == 2
assert len(benchmarks['IborFRA']) == 1
assert len(benchmarks['IborSwap']) == 10


if __name__ == '__main__':
test_ibor_benchmarks_report()
test_dataframe_to_benchmarks()
89 changes: 89 additions & 0 deletions golden_tests/TestFinIborCurveParRateShock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import pytest
import pandas as pd

from financepy.utils.global_types import SwapTypes
from financepy.utils.math import ONE_MILLION
from financepy.utils.global_vars import gBasisPoint
from financepy.market.curves.interpolator import InterpTypes
from financepy.products.rates.ibor_swap import IborSwap
from financepy.products.rates.ibor_fra import IborFRA
from financepy.products.rates.ibor_deposit import IborDeposit
from financepy.products.rates.ibor_future import IborFuture
from financepy.products.rates.ibor_single_curve import IborSingleCurve
from financepy.products.rates.ibor_single_curve_par_shocker import IborSingleCurveParShocker
from financepy.utils.frequency import FrequencyTypes
from financepy.utils.day_count import DayCountTypes
from financepy.utils.date import Date
from financepy.utils.calendar import Calendar, CalendarTypes


def test_ibor_curve_par_rate_shocker():
valuation_date = Date(6, 10, 2001)
cal = CalendarTypes.UNITED_KINGDOM
interp_type = InterpTypes.FLAT_FWD_RATES

depoDCCType = DayCountTypes.ACT_360
depos = []
spot_days = 2
settlement_date = valuation_date.add_weekdays(spot_days)
depo = IborDeposit(settlement_date, "3M", 4.2/100.0, depoDCCType, cal_type=cal)
depos.append(depo)

fraDCCType = DayCountTypes.ACT_360
fras = []
fra = IborFRA(settlement_date.add_tenor("3M"), "3M", 4.20/100.0, fraDCCType, cal_type=cal)
fras.append(fra)

swaps = []
swapType = SwapTypes.PAY
fixedDCCType = DayCountTypes.THIRTY_E_360_ISDA
fixedFreqType = FrequencyTypes.SEMI_ANNUAL

swap = IborSwap(settlement_date, "1Y", swapType, 4.20/100.0, fixedFreqType, fixedDCCType, cal_type=cal)
swaps.append(swap)
swap = IborSwap(settlement_date, "2Y", swapType, 4.30/100.0, fixedFreqType, fixedDCCType, cal_type=cal)
swaps.append(swap)
swap = IborSwap(settlement_date, "3Y", swapType, 4.70/100.0, fixedFreqType, fixedDCCType, cal_type=cal)
swaps.append(swap)

base_curve = IborSingleCurve(valuation_date, depos, fras, swaps, InterpTypes.FLAT_FWD_RATES, )
curve_shocker = IborSingleCurveParShocker(base_curve)
mat_dates = curve_shocker.benchmarks_report()['maturity_date'].values

# size of bump
par_rate_bump = 1*gBasisPoint

# expected forward rate changes in the periods before and after the maturity date of the bumped benchmark
# in basis points
expected_fwd_rate_changes = {1: (1.00000, 0.0),
2: (1.00000, -0.50715),
4: (2.06214, -2.16773)}

# Which benchmarks we test-bump. They are
# - The first (and only) depo we used in construction. Here index = 1 (not 0) because
# ibor_single_curve creates a synthetic deposit for the stub implictly
# - The only FRA
# - the 2Y swap
benchmark_idxs = [1, 2, 4]
for benchmark_idx in benchmark_idxs:
bumped_curve = curve_shocker.apply_bump_to_benchmark(benchmark_idx, par_rate_bump)

d1 = mat_dates[benchmark_idx-1]
d2 = mat_dates[benchmark_idx]
d3 = mat_dates[benchmark_idx+1]

base_fwd_before = base_curve.fwd_rate(d1, d2)
base_fwd_after = base_curve.fwd_rate(d2, d3)
bumped_fwd_before = bumped_curve.fwd_rate(d1, d2)
bumped_fwd_after = bumped_curve.fwd_rate(d2, d3)

actual_fwd_rate_changes = ((bumped_fwd_before-base_fwd_before)/gBasisPoint,
(bumped_fwd_after-base_fwd_after)/gBasisPoint)

assert round(actual_fwd_rate_changes[0], 3) == round(expected_fwd_rate_changes[benchmark_idx][0], 3)
assert round(actual_fwd_rate_changes[1], 3) == round(expected_fwd_rate_changes[benchmark_idx][1], 3)


if __name__ == '__main__':
test_ibor_curve_par_rate_shocker();

Loading

0 comments on commit d4bd48c

Please sign in to comment.