From 34e318182e468004dff828817661576213a3ad28 Mon Sep 17 00:00:00 2001 From: Dominic O'Kane Date: Mon, 19 Feb 2024 21:07:05 +0100 Subject: [PATCH] Fixed problem with Gaussian Loss Dbn. --- financepy/__init__.py | 2 +- financepy/models/gauss_copula_onefactor.py | 4 +- financepy/products/bonds/__init__.py | 6 +- financepy/products/bonds/bond.py | 16 +- .../{yield_curve.py => bond_yield_curve.py} | 369 ++++++++-------- .../{zero_curve.py => bond_zero_curve.py} | 400 +++++++++--------- .../{yield_curve_model.py => curve_fits.py} | 380 ++++++++--------- financepy/products/credit/cds_curve.py | 2 +- financepy/products/credit/cds_tranche.py | 39 +- .../FINBONDYIELDCURVES_FittingExample.ipynb | 6 +- ...YIELDCURVE_FittingToBondMarketPrices.ipynb | 10 +- tests/test_FinBond.py | 13 +- tests/test_FinBondYieldCurve.py | 32 +- tests/test_FinBondZeroCurve.py | 2 +- tests/test_FinCDSTranche.py | 4 +- tests_golden/TestFinBond.py | 8 +- tests_golden/TestFinBondYieldCurve.py | 54 +-- tests_golden/TestFinBondZeroCoupon.py | 2 +- tests_golden/TestFinBondZeroCurve.py | 3 +- .../golden/TestFinCDSTranche_GOLDEN.testLog | 114 ++--- 20 files changed, 729 insertions(+), 737 deletions(-) rename financepy/products/bonds/{yield_curve.py => bond_yield_curve.py} (78%) rename financepy/products/bonds/{zero_curve.py => bond_zero_curve.py} (97%) rename financepy/products/bonds/{yield_curve_model.py => curve_fits.py} (96%) diff --git a/financepy/__init__.py b/financepy/__init__.py index 66a8a6435..f54acc716 100644 --- a/financepy/__init__.py +++ b/financepy/__init__.py @@ -1,7 +1,7 @@ cr = "\n" s = "####################################################################" + cr -s += "# FINANCEPY BETA Version " + str('0.34') + " - This build: 19 Feb 2024 at 04:06 #" + cr +s += "# FINANCEPY BETA Version " + str('0.34') + " - This build: 19 Feb 2024 at 21:02 #" + 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 += "####################################################################" diff --git a/financepy/models/gauss_copula_onefactor.py b/financepy/models/gauss_copula_onefactor.py index 58d359784..0af5741ad 100644 --- a/financepy/models/gauss_copula_onefactor.py +++ b/financepy/models/gauss_copula_onefactor.py @@ -218,7 +218,9 @@ def gauss_approx_tranche_loss(k1, k2, mu, sigma): d1 = (mu - k1) / sigma d2 = (mu - k2) / sigma - gauss_approx_tranche_loss = (mu - k1) * N(d1) - (mu - k2) * N(d2) + sigma * np.exp(-0.5 * d1 * d1) * INVROOT2PI - sigma * np.exp(-0.5 * d2 * d2) * INVROOT2PI + gauss_approx_tranche_loss = ((mu - k1) * N(d1) - (mu - k2) * N(d2) + + sigma * np.exp(-0.5 * d1 * d1) * INVROOT2PI + - sigma * np.exp(-0.5 * d2 * d2) * INVROOT2PI) return gauss_approx_tranche_loss diff --git a/financepy/products/bonds/__init__.py b/financepy/products/bonds/__init__.py index 8dedf14f3..f20966c78 100644 --- a/financepy/products/bonds/__init__.py +++ b/financepy/products/bonds/__init__.py @@ -1,13 +1,13 @@ from .bond import * from .bond_zero import * from .bond_annuity import * -from .zero_curve import * +from .bond_zero_curve import * from .bond_convertible import * from .bond_callable import * from .bond_frn import * from .bond_future import * from .bond_market import * from .bond_option import * -from .yield_curve import * -from .yield_curve_model import * +from .bond_yield_curve import * +from .curve_fits import * from .bond_mortgage import * diff --git a/financepy/products/bonds/bond.py b/financepy/products/bonds/bond.py index 333dc6e06..787c58e47 100644 --- a/financepy/products/bonds/bond.py +++ b/financepy/products/bonds/bond.py @@ -38,7 +38,7 @@ from ...utils.math import npv from ...market.curves.discount_curve import DiscountCurve from ...market.curves.interpolator import InterpTypes -from .zero_curve import BondZeroCurve +from .bond_zero_curve import BondZeroCurve # References https://www.dmo.gov.uk/media/15011/yldeqns_v1.pdf # DO TRUE YIELD @@ -390,7 +390,6 @@ def modified_duration(self, ########################################################################### def key_rate_durations(self, - bond, settle_dt: Date, ytm: float, key_rate_tenors: list = None, @@ -455,7 +454,7 @@ def key_rate_durations(self, mat_dt = settle_dt.add_years(tenor) par_bond = Bond(settle_dt, mat_dt, cpn, - bond._freq_type, bond._dc_type) + self._freq_type, self._dc_type) par_bonds.append(par_bond) @@ -473,7 +472,7 @@ def key_rate_durations(self, lin_zero_interp) # calculate the dirty price of the bond using the discount curve - p_zero = bond.dirty_price_from_discount_curve(settle_dt, + p_zero = self.dirty_price_from_discount_curve(settle_dt, par_crv) # shift up by the yield of corresponding par bond @@ -485,7 +484,8 @@ def key_rate_durations(self, mat = settle_dt.add_years(tenor) par_bond = Bond(settle_dt, mat, cpn, - bond._freq_type, bond._dc_type) + self._freq_type, + self._dc_type) par_bonds.append(par_bond) @@ -504,7 +504,7 @@ def key_rate_durations(self, # calculate the full price of the bond # using the discount curve with the key rate shifted up - p_up = bond.dirty_price_from_discount_curve( + p_up = self.dirty_price_from_discount_curve( settle_dt, par_crv_up) # create a curve again with the key rate shifted down @@ -517,7 +517,7 @@ def key_rate_durations(self, mat = settle_dt.add_years(tenor) par_bond = Bond(settle_dt, mat, cpn, - bond._freq_type, bond._dc_type) + self._freq_type, self._dc_type) par_bonds.append(par_bond) @@ -535,7 +535,7 @@ def key_rate_durations(self, lin_zero_interp) # calculate the full price of the bond using - p_down = bond.dirty_price_from_discount_curve( + p_down = self.dirty_price_from_discount_curve( settle_dt, par_crv_down) # calculate the key rate duration diff --git a/financepy/products/bonds/yield_curve.py b/financepy/products/bonds/bond_yield_curve.py similarity index 78% rename from financepy/products/bonds/yield_curve.py rename to financepy/products/bonds/bond_yield_curve.py index 0f0299ddf..290130966 100644 --- a/financepy/products/bonds/yield_curve.py +++ b/financepy/products/bonds/bond_yield_curve.py @@ -1,184 +1,185 @@ -############################################################################## -# Copyright (C) 2018, 2019, 2020 Dominic O'Kane -############################################################################## - - -import numpy as np -import matplotlib.pyplot as plt - -from scipy.interpolate import splrep - -from ...utils.error import FinError -from ...utils.date import Date -from ...utils.global_vars import gDaysInYear -from ...utils.math import scale -from ...utils.helpers import label_to_string - -from .yield_curve_model import CurveFitPolynomial -from .yield_curve_model import CurveFitNelsonSiegel -from .yield_curve_model import CurveFitNelsonSiegelSvensson -from .yield_curve_model import CurveFitBSpline - -############################################################################### -# TO DO: CONSTRAIN TAU'S IN NELSON-SIEGEL -############################################################################### - - -class BondYieldCurve(): - """ Class to do fitting of the yield curve and to enable interpolation of - yields. Because yields assume a flat term structure for each bond, this - class does not allow discounting to be done and so does not inherit from - FinDiscountCurve. It should only be used for visualisation and simple - interpolation but not for full term-structure-consistent pricing. """ - - def __init__(self, - settle_dt: Date, - bonds: list, - ylds: (np.ndarray, list), - curve_fit): - """ Fit the curve to a set of bond yields using the type of curve - specified. Bounds can be provided if you wish to enforce lower and - upper limits on the respective model parameters. """ - - self._settle_dt = settle_dt - self._bonds = bonds - self._ylds = np.array(ylds) - self._curve_fit = curve_fit - - fit_type = type(self._curve_fit) - fit = self._curve_fit - - yearsToMaturities = [] - for bond in bonds: - years_to_maturity = (bond._maturity_dt - - settle_dt)/gDaysInYear - yearsToMaturities.append(years_to_maturity) - self._yearsToMaturity = np.array(yearsToMaturities) - - if fit_type is CurveFitPolynomial: - - d = fit._power - coeffs = np.polyfit(self._yearsToMaturity, self._ylds, deg=d) - fit._coeffs = coeffs - - elif fit_type is CurveFitNelsonSiegel: - - xdata = self._yearsToMaturity - ydata = self._ylds - - popt, pcov = curve_fit(self._curve_fit._interpolated_yield, - xdata, ydata, bounds=fit._bounds) - - fit._beta_1 = popt[0] - fit._beta_2 = popt[1] - fit._beta_3 = popt[2] - fit._tau = popt[3] - - elif fit_type is CurveFitNelsonSiegelSvensson: - - xdata = self._yearsToMaturity - ydata = self._ylds - - popt, pcov = curve_fit(self._curve_fit._interpolated_yield, - xdata, ydata, bounds=fit._bounds) - - fit._beta_1 = popt[0] - fit._beta_2 = popt[1] - fit._beta_3 = popt[2] - fit._beta_4 = popt[3] - fit._tau_1 = popt[4] - fit._tau_2 = popt[5] - - elif fit_type is CurveFitBSpline: - - xdata = self._yearsToMaturity - ydata = self._ylds - - """ Cubic splines as k=3 """ - spline = splrep(xdata, ydata, k=fit._power, t=fit._knots) - fit._spline = spline - - else: - raise FinError("Unrecognised curve fit type.") - -############################################################################### - - def interpolated_yield(self, - maturity_dt: Date): - - if isinstance(maturity_dt, Date): - t = (maturity_dt - self._settle_dt) / gDaysInYear - elif isinstance(maturity_dt, list): - t = maturity_dt - elif isinstance(maturity_dt, np.ndarray): - t = maturity_dt - elif isinstance(maturity_dt, float) or type(maturity_dt) is np.float64: - t = maturity_dt - else: - raise FinError("Unknown date type.") - - fit = self._curve_fit - - if isinstance(fit, CurveFitPolynomial): - yld = fit._interpolated_yield(t) - elif isinstance(fit, CurveFitNelsonSiegel): - yld = fit._interpolated_yield(t, - fit._beta_1, - fit._beta_2, - fit._beta_3, - fit._tau) - - elif isinstance(fit) == CurveFitNelsonSiegelSvensson: - yld = fit._interpolated_yield(t, - fit._beta_1, - fit._beta_2, - fit._beta_3, - fit._beta_4, - fit._tau_1, - fit._tau_2) - - elif isinstance(fit) == CurveFitBSpline: - yld = fit._interpolated_yield(t) - - return yld - -############################################################################### - - def plot(self, - title): - """ Display yield curve. """ - - plt.figure(figsize=(12, 6)) - plt.title(title) - bond_ylds_scaled = scale(self._ylds, 100.0) - plt.plot(self._yearsToMaturity, bond_ylds_scaled, 'o') - plt.xlabel('Time to Maturity (years)') - plt.ylabel('Yield To Maturity (%)') - - tmax = np.max(self._yearsToMaturity) - t = np.linspace(0.0, int(tmax+0.5), 100) - - yld = self.interpolated_yield(t) - yld = scale(yld, 100.0) - plt.plot(t, yld, label=str(self._curve_fit)) - plt.legend(loc='lower right') - plt.ylim((min(yld)-0.3, max(yld)*1.1)) - plt.grid(True) - -############################################################################### - - def __repr__(self): - s = label_to_string("OBJECT TYPE", type(self).__name__) - s += label_to_string("SETTLEMENT DATE", self._settle_dt) - s += label_to_string("BOND", self._bonds) - s += label_to_string("YIELDS", self._ylds) - s += label_to_string("CURVE FIT", self._curve_fit) - return s - -############################################################################### - - def _print(self): - """ Simple print function for backward compatibility. """ - print(self) - -############################################################################## +############################################################################## +# Copyright (C) 2018, 2019, 2020 Dominic O'Kane +############################################################################## + + +import numpy as np +import matplotlib.pyplot as plt + +import scipy +from scipy.interpolate import splrep + +from ...utils.error import FinError +from ...utils.date import Date +from ...utils.global_vars import gDaysInYear +from ...utils.math import scale +from ...utils.helpers import label_to_string + +from .curve_fits import CurveFitPolynomial +from .curve_fits import CurveFitNelsonSiegel +from .curve_fits import CurveFitNelsonSiegelSvensson +from .curve_fits import CurveFitBSpline + +############################################################################### +# TO DO: CONSTRAIN TAU'S IN NELSON-SIEGEL +############################################################################### + + +class BondYieldCurve(): + """ Class to do fitting of the yield curve and to enable interpolation of + yields. Because yields assume a flat term structure for each bond, this + class does not allow discounting to be done and so does not inherit from + FinDiscountCurve. It should only be used for visualisation and simple + interpolation but not for full term-structure-consistent pricing. """ + + def __init__(self, + settle_dt: Date, + bonds: list, + ylds: (np.ndarray, list), + curve_fit): + """ Fit the curve to a set of bond yields using the type of curve + specified. Bounds can be provided if you wish to enforce lower and + upper limits on the respective model parameters. """ + + self._settle_dt = settle_dt + self._bonds = bonds + self._ylds = np.array(ylds) + self._curve_fit = curve_fit + + fit_type = type(self._curve_fit) + + yearsToMaturities = [] + + for bond in bonds: + years_to_maturity = (bond._maturity_dt - settle_dt)/gDaysInYear + yearsToMaturities.append(years_to_maturity) + + self._yearsToMaturity = np.array(yearsToMaturities) + + if fit_type is CurveFitPolynomial: + + d = curve_fit._power + coeffs = np.polyfit(self._yearsToMaturity, self._ylds, deg=d) + curve_fit._coeffs = coeffs + + elif fit_type is CurveFitNelsonSiegel: + + xdata = self._yearsToMaturity + ydata = self._ylds + + popt, pcov = scipy.optimize.curve_fit(curve_fit._interpolated_yield, + xdata, ydata, bounds=curve_fit._bounds) + + curve_fit._beta_1 = popt[0] + curve_fit._beta_2 = popt[1] + curve_fit._beta_3 = popt[2] + curve_fit._tau = popt[3] + + elif fit_type is CurveFitNelsonSiegelSvensson: + + xdata = self._yearsToMaturity + ydata = self._ylds + + popt, pcov = scipy.optimize.curve_fit(curve_fit._interpolated_yield, + xdata, ydata, bounds=curve_fit._bounds) + + curve_fit._beta_1 = popt[0] + curve_fit._beta_2 = popt[1] + curve_fit._beta_3 = popt[2] + curve_fit._beta_4 = popt[3] + curve_fit._tau_1 = popt[4] + curve_fit._tau_2 = popt[5] + + elif fit_type is CurveFitBSpline: + + xdata = self._yearsToMaturity + ydata = self._ylds + + """ Cubic splines as k=3 """ + spline = splrep(xdata, ydata, k=curve_fit._power, t=curve_fit._knots) + self._curve_fit._spline = spline + + else: + raise FinError("Unrecognised curve fit type.") + +############################################################################### + + def interpolated_yield(self, + maturity_dt: Date): + + if isinstance(maturity_dt, Date): + t = (maturity_dt - self._settle_dt) / gDaysInYear + elif isinstance(maturity_dt, list): + t = maturity_dt + elif isinstance(maturity_dt, np.ndarray): + t = maturity_dt + elif isinstance(maturity_dt, float) or type(maturity_dt) is np.float64: + t = maturity_dt + else: + raise FinError("Unknown date type.") + + fit = self._curve_fit + + if isinstance(fit, CurveFitPolynomial): + yld = fit._interpolated_yield(t) + elif isinstance(fit, CurveFitNelsonSiegel): + yld = fit._interpolated_yield(t, + fit._beta_1, + fit._beta_2, + fit._beta_3, + fit._tau) + + elif isinstance(fit, CurveFitNelsonSiegelSvensson): + yld = fit._interpolated_yield(t, + fit._beta_1, + fit._beta_2, + fit._beta_3, + fit._beta_4, + fit._tau_1, + fit._tau_2) + + elif isinstance(fit, CurveFitBSpline): + yld = fit._interpolated_yield(t) + + return yld + +############################################################################### + + def plot(self, + title): + """ Display yield curve. """ + + plt.figure(figsize=(12, 6)) + plt.title(title) + bond_ylds_scaled = scale(self._ylds, 100.0) + plt.plot(self._yearsToMaturity, bond_ylds_scaled, 'o') + plt.xlabel('Time to Maturity (years)') + plt.ylabel('Yield To Maturity (%)') + + tmax = np.max(self._yearsToMaturity) + t = np.linspace(0.0, int(tmax+0.5), 100) + + yld = self.interpolated_yield(t) + yld = scale(yld, 100.0) + plt.plot(t, yld, label=str(self._curve_fit)) + plt.legend(loc='lower right') + plt.ylim((min(yld)-0.3, max(yld)*1.1)) + plt.grid(True) + +############################################################################### + + def __repr__(self): + s = label_to_string("OBJECT TYPE", type(self).__name__) + s += label_to_string("SETTLEMENT DATE", self._settle_dt) + s += label_to_string("BOND", self._bonds) + s += label_to_string("YIELDS", self._ylds) + s += label_to_string("CURVE FIT", self._curve_fit) + return s + +############################################################################### + + def _print(self): + """ Simple print function for backward compatibility. """ + print(self) + +############################################################################## diff --git a/financepy/products/bonds/zero_curve.py b/financepy/products/bonds/bond_zero_curve.py similarity index 97% rename from financepy/products/bonds/zero_curve.py rename to financepy/products/bonds/bond_zero_curve.py index 8bde80fcd..37949bb05 100644 --- a/financepy/products/bonds/zero_curve.py +++ b/financepy/products/bonds/bond_zero_curve.py @@ -1,200 +1,200 @@ -############################################################################## -# Copyright (C) 2018, 2019, 2020 Dominic O'Kane -############################################################################## - - -import numpy as np -import matplotlib.pyplot as plt -from scipy import optimize - -from ...utils.date import Date -from ...utils.math import scale, test_monotonicity -from ...utils.global_vars import gDaysInYear -from ...utils.day_count import DayCount, DayCountTypes -from ...utils.helpers import input_time -from ...utils.helpers import table_to_string -from ...market.curves.interpolator import InterpTypes, interpolate -from ...utils.error import FinError -from ...utils.frequency import annual_frequency, FrequencyTypes -from ...market.curves.discount_curve import DiscountCurve -from ...utils.helpers import label_to_string - -############################################################################### - - -def _f(df, *args): - curve = args[0] - value_dt = args[1] - bond = args[2] - marketCleanPrice = args[3] - num_points = len(curve._times) - curve._values[num_points - 1] = df - bondDiscountPrice = bond.clean_price_from_discount_curve( - value_dt, curve) - obj_fn = bondDiscountPrice - marketCleanPrice - return obj_fn - -############################################################################### - - -class BondZeroCurve(DiscountCurve): - """ Class to do bootstrap exact fitting of the bond zero rate curve. """ - - def __init__(self, - value_dt: Date, - bonds: list, - clean_prices: list, - interp_type: InterpTypes = InterpTypes.FLAT_FWD_RATES): - """ Fit a discount curve to a set of bond yields using the type of - curve specified. """ - - if len(bonds) != len(clean_prices): - raise FinError("Num bonds does not equal number of prices.") - - self._settle_dt = value_dt - self._value_dt = value_dt - self._bonds = bonds - self._clean_prices = np.array(clean_prices) - self._discount_curve = None - self._interp_type = interp_type - - times = [] - for bond in self._bonds: - t_mat = (bond._maturity_dt - self._settle_dt)/gDaysInYear - times.append(t_mat) - - times = np.array(times) - if test_monotonicity(times) is False: - raise FinError("Times are not sorted in increasing order") - - self._yearsToMaturity = np.array(times) - - self._bootstrap_zero_rates() - -############################################################################### - - def _bootstrap_zero_rates(self): - - self._times = np.array([0.0]) - self._values = np.array([1.0]) - df = 1.0 - - for i in range(0, len(self._bonds)): - bond = self._bonds[i] - maturity_dt = bond._maturity_dt - clean_price = self._clean_prices[i] - t_mat = (maturity_dt - self._settle_dt) / gDaysInYear - argtuple = (self, self._settle_dt, bond, clean_price) - self._times = np.append(self._times, t_mat) - self._values = np.append(self._values, df) - - optimize.newton(_f, x0=df, fprime=None, args=argtuple, - tol=1e-8, maxiter=100, fprime2=None) - -############################################################################### - - def zero_rate(self, - dt: Date, - frequencyType: FrequencyTypes = FrequencyTypes.CONTINUOUS): - """ Calculate the zero rate to maturity date. """ - t = input_time(dt, self) - f = annual_frequency(frequencyType) - df = self.df(t) - - if f == 0: # Simple interest - zero_rate = (1.0/df-1.0)/t - if f == -1: # Continuous - zero_rate = -np.log(df) / t - else: - zero_rate = (df**(-1.0/t) - 1) * f - return zero_rate - -############################################################################### - - def df(self, - dt: Date): - t = input_time(dt, self) - z = interpolate(t, self._times, self._values, self._interp_type.value) - return z - -############################################################################### - - def survival_prob(self, - dt: Date): - t = input_time(dt, self) - q = interpolate(t, self._times, self._values, self._interp_type.value) - return q - -############################################################################### - - def fwd(self, - dt: Date): - """ Calculate the continuous forward rate at the forward date. """ - t = input_time(dt, self) - dt = 0.000001 - df1 = self.df(t) - df2 = self.df(t+dt) - fwd = np.log(df1/df2)/dt - return fwd - -############################################################################### - - def fwd_rate(self, - date1: Date, - date2: Date, - day_count_type: DayCountTypes): - """ Calculate the forward rate according to the specified - day count convention. """ - - if date1 < self._value_dt: - raise FinError("Date1 before curve value date.") - - if date2 < date1: - raise FinError("Date2 must not be before Date1") - - day_count = DayCount(day_count_type) - year_frac = day_count.year_frac(date1, date2)[0] - df1 = self.df(date1) - df2 = self.df(date2) - fwd = (df1 / df2 - 1.0) / year_frac - return fwd - -############################################################################### - - def plot(self, - title: str): - """ Display yield curve. """ - - plt.figure(figsize=(12, 6)) - plt.title(title) - plt.xlabel('Time to Maturity (years)') - plt.ylabel('Zero Rate (%)') - - tmax = np.max(self._yearsToMaturity) - t = np.linspace(0.0, int(tmax+0.5), 100) - - zero_rate = self.zero_rate(t) - zero_rate = scale(zero_rate, 100.0) - plt.plot(t, zero_rate, label="Zero Rate Bootstrap", marker='o') - plt.legend(loc='lower right') - plt.ylim((min(zero_rate)-0.3, max(zero_rate)*1.1)) - plt.grid(True) - -############################################################################### - - def __repr__(self): - # TODO - header = "TIMES,DISCOUNT FACTORS" - s = label_to_string("OBJECT TYPE", type(self).__name__) - valueTable = [self._times, self._values] - precision = "10.7f" - s += table_to_string(header, valueTable, precision) - return s - -############################################################################### - - def _print(self): - """ Simple print function for backward compatibility. """ - print(self) - -############################################################################### +############################################################################## +# Copyright (C) 2018, 2019, 2020 Dominic O'Kane +############################################################################## + + +import numpy as np +import matplotlib.pyplot as plt +from scipy import optimize + +from ...utils.date import Date +from ...utils.math import scale, test_monotonicity +from ...utils.global_vars import gDaysInYear +from ...utils.day_count import DayCount, DayCountTypes +from ...utils.helpers import input_time +from ...utils.helpers import table_to_string +from ...market.curves.interpolator import InterpTypes, interpolate +from ...utils.error import FinError +from ...utils.frequency import annual_frequency, FrequencyTypes +from ...market.curves.discount_curve import DiscountCurve +from ...utils.helpers import label_to_string + +############################################################################### + + +def _f(df, *args): + curve = args[0] + value_dt = args[1] + bond = args[2] + marketCleanPrice = args[3] + num_points = len(curve._times) + curve._values[num_points - 1] = df + bondDiscountPrice = bond.clean_price_from_discount_curve( + value_dt, curve) + obj_fn = bondDiscountPrice - marketCleanPrice + return obj_fn + +############################################################################### + + +class BondZeroCurve(DiscountCurve): + """ Class to do bootstrap exact fitting of the bond zero rate curve. """ + + def __init__(self, + value_dt: Date, + bonds: list, + clean_prices: list, + interp_type: InterpTypes = InterpTypes.FLAT_FWD_RATES): + """ Fit a discount curve to a set of bond yields using the type of + curve specified. """ + + if len(bonds) != len(clean_prices): + raise FinError("Num bonds does not equal number of prices.") + + self._settle_dt = value_dt + self._value_dt = value_dt + self._bonds = bonds + self._clean_prices = np.array(clean_prices) + self._discount_curve = None + self._interp_type = interp_type + + times = [] + for bond in self._bonds: + t_mat = (bond._maturity_dt - self._settle_dt)/gDaysInYear + times.append(t_mat) + + times = np.array(times) + if test_monotonicity(times) is False: + raise FinError("Times are not sorted in increasing order") + + self._yearsToMaturity = np.array(times) + + self._bootstrap_zero_rates() + +############################################################################### + + def _bootstrap_zero_rates(self): + + self._times = np.array([0.0]) + self._values = np.array([1.0]) + df = 1.0 + + for i in range(0, len(self._bonds)): + bond = self._bonds[i] + maturity_dt = bond._maturity_dt + clean_price = self._clean_prices[i] + t_mat = (maturity_dt - self._settle_dt) / gDaysInYear + argtuple = (self, self._settle_dt, bond, clean_price) + self._times = np.append(self._times, t_mat) + self._values = np.append(self._values, df) + + optimize.newton(_f, x0=df, fprime=None, args=argtuple, + tol=1e-8, maxiter=100, fprime2=None) + +############################################################################### + + def zero_rate(self, + dt: Date, + frequencyType: FrequencyTypes = FrequencyTypes.CONTINUOUS): + """ Calculate the zero rate to maturity date. """ + t = input_time(dt, self) + f = annual_frequency(frequencyType) + df = self.df(t) + + if f == 0: # Simple interest + zero_rate = (1.0/df-1.0)/t + if f == -1: # Continuous + zero_rate = -np.log(df) / t + else: + zero_rate = (df**(-1.0/t) - 1) * f + return zero_rate + +############################################################################### + + def df(self, + dt: Date): + t = input_time(dt, self) + z = interpolate(t, self._times, self._values, self._interp_type.value) + return z + +############################################################################### + + def survival_prob(self, + dt: Date): + t = input_time(dt, self) + q = interpolate(t, self._times, self._values, self._interp_type.value) + return q + +############################################################################### + + def fwd(self, + dt: Date): + """ Calculate the continuous forward rate at the forward date. """ + t = input_time(dt, self) + dt = 0.000001 + df1 = self.df(t) + df2 = self.df(t+dt) + fwd = np.log(df1/df2)/dt + return fwd + +############################################################################### + + def fwd_rate(self, + date1: Date, + date2: Date, + day_count_type: DayCountTypes): + """ Calculate the forward rate according to the specified + day count convention. """ + + if date1 < self._value_dt: + raise FinError("Date1 before curve value date.") + + if date2 < date1: + raise FinError("Date2 must not be before Date1") + + day_count = DayCount(day_count_type) + year_frac = day_count.year_frac(date1, date2)[0] + df1 = self.df(date1) + df2 = self.df(date2) + fwd = (df1 / df2 - 1.0) / year_frac + return fwd + +############################################################################### + + def plot(self, + title: str): + """ Display yield curve. """ + + plt.figure(figsize=(12, 6)) + plt.title(title) + plt.xlabel('Time to Maturity (years)') + plt.ylabel('Zero Rate (%)') + + tmax = np.max(self._yearsToMaturity) + t = np.linspace(0.0, int(tmax+0.5), 100) + + zero_rate = self.zero_rate(t) + zero_rate = scale(zero_rate, 100.0) + plt.plot(t, zero_rate, label="Zero Rate Bootstrap", marker='o') + plt.legend(loc='lower right') + plt.ylim((min(zero_rate)-0.3, max(zero_rate)*1.1)) + plt.grid(True) + +############################################################################### + + def __repr__(self): + # TODO + header = "TIMES,DISCOUNT FACTORS" + s = label_to_string("OBJECT TYPE", type(self).__name__) + valueTable = [self._times, self._values] + precision = "10.7f" + s += table_to_string(header, valueTable, precision) + return s + +############################################################################### + + def _print(self): + """ Simple print function for backward compatibility. """ + print(self) + +############################################################################### diff --git a/financepy/products/bonds/yield_curve_model.py b/financepy/products/bonds/curve_fits.py similarity index 96% rename from financepy/products/bonds/yield_curve_model.py rename to financepy/products/bonds/curve_fits.py index 5b13dd2d4..a8be8654d 100644 --- a/financepy/products/bonds/yield_curve_model.py +++ b/financepy/products/bonds/curve_fits.py @@ -1,190 +1,190 @@ -############################################################################## -# Copyright (C) 2018, 2019, 2020 Dominic O'Kane -############################################################################## - - -import numpy as np -from scipy.interpolate import splev -from ...utils.helpers import label_to_string - -############################################################################### - - -class FinCurveFitMethod(): - pass - -############################################################################### - - -class CurveFitPolynomial(): - - def __init__(self, power=3): - self._parent_type = FinCurveFitMethod - self._power = power - self._coeffs = [] - - def _interpolated_yield(self, t): - yld = np.polyval(self._coeffs, t) - return yld - - def __repr__(self): - s = label_to_string("OBJECT TYPE", type(self).__name__) - s += label_to_string("Power", self._power) - - for c in self._coeffs: - s += label_to_string("Coefficient", c) - - return s - - def _print(self): - """ Simple print function for backward compatibility. """ - print(self) - -############################################################################### - - -class CurveFitNelsonSiegel(): - - def __init__(self, tau=None, bounds=[(-1, -1, -1, 0.5), (1, 1, 1, 100)]): - self._parent_type = FinCurveFitMethod - self._beta_1 = None - self._beta_2 = None - self._beta_3 = None - self._tau = tau - """ Fairly permissive bounds. Only tau_1 is 1-100 """ - self._bounds = bounds - - def _interpolated_yield(self, t, beta_1=None, beta_2=None, - beta_3=None, tau=None): - - t = np.maximum(t, 1e-10) - - if beta_1 is None: - beta_1 = self._beta_1 - - if beta_2 is None: - beta_2 = self._beta_2 - - if beta_3 is None: - beta_3 = self._beta_3 - - if tau is None: - tau = self._tau - - theta = t / tau - exp_term = np.exp(-theta) - yld = beta_1 - yld += beta_2 * (1.0 - exp_term) / theta - yld += beta_3 * ((1.0 - exp_term) / theta - exp_term) - return yld - - def __repr__(self): - s = label_to_string("OBJECT TYPE", type(self).__name__) - s += label_to_string("beta_1", self._beta_1) - s += label_to_string("beta_2", self._beta_2) - s += label_to_string("beta_3", self._beta_3) - s += label_to_string("Tau", self._tau) - return s - - def _print(self): - """ Simple print function for backward compatibility. """ - print(self) - -############################################################################### - - -class CurveFitNelsonSiegelSvensson(): - - def __init__(self, tau_1=None, tau_2=None, - bounds=[(0, -1, -1, -1, 0, 1), (1, 1, 1, 1, 10, 100)]): - """ Create object to store calibration and functional form of NSS - parametric fit. """ - - self._parent_type = FinCurveFitMethod - self._beta_1 = None - self._beta_2 = None - self._beta_3 = None - self._beta_4 = None - self._tau_1 = tau_1 - self._tau_2 = tau_2 - - """ I impose some bounds to help ensure a sensible result if - the user does not provide any bounds. Especially for tau_2. """ - self._bounds = bounds - - def _interpolated_yield(self, t, beta_1=None, beta_2=None, beta_3=None, - beta_4=None, tau_1=None, tau_2=None): - - # Careful if we get a time zero point - t = np.maximum(t, 1e-10) - - if beta_1 is None: - beta_1 = self._beta_1 - - if beta_2 is None: - beta_2 = self._beta_2 - - if beta_3 is None: - beta_3 = self._beta_3 - - if beta_4 is None: - beta_4 = self._beta_4 - - if tau_1 is None: - tau_1 = self._tau_1 - - if tau_2 is None: - tau_2 = self._tau_2 - - theta1 = t / tau_1 - theta2 = t / tau_2 - exp_term1 = np.exp(-theta1) - exp_term2 = np.exp(-theta2) - yld = beta_1 - yld += beta_2 * (1.0 - exp_term1) / theta1 - yld += beta_3 * ((1.0 - exp_term1) / theta1 - exp_term1) - yld += beta_4 * ((1.0 - exp_term2) / theta2 - exp_term2) - return yld - - def __repr__(self): - s = label_to_string("OBJECT TYPE", type(self).__name__) - s += label_to_string("beta_1", self._beta_1) - s += label_to_string("beta_2", self._beta_2) - s += label_to_string("beta_3", self._beta_3) - s += label_to_string("beta_4", self._beta_3) - s += label_to_string("tau_1", self._tau_1) - s += label_to_string("tau_2", self._tau_2) - return s - - def _print(self): - """ Simple print function for backward compatibility. """ - print(self) - -############################################################################### - - -class CurveFitBSpline(): - - def __init__(self, power=3, knots=[1, 3, 5, 10]): - self._parent_type = FinCurveFitMethod - self._power = power - self._knots = knots - self._spline = None - - def _interpolated_yield(self, t): - t = np.maximum(t, 1e-10) - yld = splev(t, self._spline) - return yld - - def __repr__(self): - s = label_to_string("OBJECT TYPE", type(self).__name__) - s += label_to_string("Power", self._power) - s += label_to_string("Knots", self._knots) - s += label_to_string("Spline", self._spline) - return s - - def _print(self): - """ Simple print function for backward compatibility. """ - print(self) - -############################################################################### +############################################################################## +# Copyright (C) 2018, 2019, 2020 Dominic O'Kane +############################################################################## + + +import numpy as np +from scipy.interpolate import splev +from ...utils.helpers import label_to_string + +############################################################################### + + +class FinCurveFitMethod(): + pass + +############################################################################### + + +class CurveFitPolynomial(): + + def __init__(self, power=3): + self._parent_type = FinCurveFitMethod + self._power = power + self._coeffs = [] + + def _interpolated_yield(self, t): + yld = np.polyval(self._coeffs, t) + return yld + + def __repr__(self): + s = label_to_string("OBJECT TYPE", type(self).__name__) + s += label_to_string("Power", self._power) + + for c in self._coeffs: + s += label_to_string("Coefficient", c) + + return s + + def _print(self): + """ Simple print function for backward compatibility. """ + print(self) + +############################################################################### + + +class CurveFitNelsonSiegel(): + + def __init__(self, tau=None, bounds=[(-1, -1, -1, 0.5), (1, 1, 1, 100)]): + self._parent_type = FinCurveFitMethod + self._beta_1 = None + self._beta_2 = None + self._beta_3 = None + self._tau = tau + """ Fairly permissive bounds. Only tau_1 is 1-100 """ + self._bounds = bounds + + def _interpolated_yield(self, t, beta_1=None, beta_2=None, + beta_3=None, tau=None): + + t = np.maximum(t, 1e-10) + + if beta_1 is None: + beta_1 = self._beta_1 + + if beta_2 is None: + beta_2 = self._beta_2 + + if beta_3 is None: + beta_3 = self._beta_3 + + if tau is None: + tau = self._tau + + theta = t / tau + exp_term = np.exp(-theta) + yld = beta_1 + yld += beta_2 * (1.0 - exp_term) / theta + yld += beta_3 * ((1.0 - exp_term) / theta - exp_term) + return yld + + def __repr__(self): + s = label_to_string("OBJECT TYPE", type(self).__name__) + s += label_to_string("beta_1", self._beta_1) + s += label_to_string("beta_2", self._beta_2) + s += label_to_string("beta_3", self._beta_3) + s += label_to_string("Tau", self._tau) + return s + + def _print(self): + """ Simple print function for backward compatibility. """ + print(self) + +############################################################################### + + +class CurveFitNelsonSiegelSvensson(): + + def __init__(self, tau_1=None, tau_2=None, + bounds=[(0, -1, -1, -1, 0, 1), (1, 1, 1, 1, 10, 100)]): + """ Create object to store calibration and functional form of NSS + parametric fit. """ + + self._parent_type = FinCurveFitMethod + self._beta_1 = None + self._beta_2 = None + self._beta_3 = None + self._beta_4 = None + self._tau_1 = tau_1 + self._tau_2 = tau_2 + + """ I impose some bounds to help ensure a sensible result if + the user does not provide any bounds. Especially for tau_2. """ + self._bounds = bounds + + def _interpolated_yield(self, t, beta_1=None, beta_2=None, beta_3=None, + beta_4=None, tau_1=None, tau_2=None): + + # Careful if we get a time zero point + t = np.maximum(t, 1e-10) + + if beta_1 is None: + beta_1 = self._beta_1 + + if beta_2 is None: + beta_2 = self._beta_2 + + if beta_3 is None: + beta_3 = self._beta_3 + + if beta_4 is None: + beta_4 = self._beta_4 + + if tau_1 is None: + tau_1 = self._tau_1 + + if tau_2 is None: + tau_2 = self._tau_2 + + theta1 = t / tau_1 + theta2 = t / tau_2 + exp_term1 = np.exp(-theta1) + exp_term2 = np.exp(-theta2) + yld = beta_1 + yld += beta_2 * (1.0 - exp_term1) / theta1 + yld += beta_3 * ((1.0 - exp_term1) / theta1 - exp_term1) + yld += beta_4 * ((1.0 - exp_term2) / theta2 - exp_term2) + return yld + + def __repr__(self): + s = label_to_string("OBJECT TYPE", type(self).__name__) + s += label_to_string("beta_1", self._beta_1) + s += label_to_string("beta_2", self._beta_2) + s += label_to_string("beta_3", self._beta_3) + s += label_to_string("beta_4", self._beta_3) + s += label_to_string("tau_1", self._tau_1) + s += label_to_string("tau_2", self._tau_2) + return s + + def _print(self): + """ Simple print function for backward compatibility. """ + print(self) + +############################################################################### + + +class CurveFitBSpline(): + + def __init__(self, power=3, knots=[1, 3, 5, 10]): + self._parent_type = FinCurveFitMethod + self._power = power + self._knots = knots + self._spline = None + + def _interpolated_yield(self, t): + t = np.maximum(t, 1e-10) + yld = splev(t, self._spline) + return yld + + def __repr__(self): + s = label_to_string("OBJECT TYPE", type(self).__name__) + s += label_to_string("Power", self._power) + s += label_to_string("Knots", self._knots) + s += label_to_string("Spline", self._spline) + return s + + def _print(self): + """ Simple print function for backward compatibility. """ + print(self) + +############################################################################### diff --git a/financepy/products/credit/cds_curve.py b/financepy/products/credit/cds_curve.py index 4ce7938aa..fd338f971 100644 --- a/financepy/products/credit/cds_curve.py +++ b/financepy/products/credit/cds_curve.py @@ -141,7 +141,7 @@ def df(self, dt): else: t = dt - df = self._libor_curve.df(t) + df = self._libor_curve._df(t) return df diff --git a/financepy/products/credit/cds_tranche.py b/financepy/products/credit/cds_tranche.py index 41978181f..ad2de17b0 100644 --- a/financepy/products/credit/cds_tranche.py +++ b/financepy/products/credit/cds_tranche.py @@ -4,8 +4,8 @@ # TODO: Add __repr__ method -import numpy as np from math import sqrt +import numpy as np from ...models.gauss_copula_onefactor import tranch_surv_prob_gaussian from ...models.gauss_copula_onefactor import tranche_surv_prob_adj_binomial @@ -34,6 +34,7 @@ class FinLossDistributionBuilder(Enum): + ''' Method for constructing loss distributions numerically ''' RECURSION = 1 ADJUSTED_BINOMIAL = 2 GAUSSIAN = 3 @@ -170,6 +171,7 @@ def value_bc(self, qt1[i] = tranche_surv_prob_recursion( 0.0, k1, num_credits, q_vector, recovery_rates, beta_vector1, num_points) + qt2[i] = tranche_surv_prob_recursion( 0.0, k2, num_credits, q_vector, recovery_rates, beta_vector2, num_points) @@ -179,36 +181,28 @@ def value_bc(self, qt1[i] = tranche_surv_prob_adj_binomial( 0.0, k1, num_credits, q_vector, recovery_rates, beta_vector1, num_points) + qt2[i] = tranche_surv_prob_adj_binomial( 0.0, k2, num_credits, q_vector, recovery_rates, beta_vector2, num_points) elif model == FinLossDistributionBuilder.GAUSSIAN: - qt1[i] = tranch_surv_prob_gaussian( - 0.0, - k1, - num_credits, - q_vector, - recovery_rates, - beta_vector1, - num_points) - qt2[i] = tranch_surv_prob_gaussian( - 0.0, - k2, - num_credits, - q_vector, - recovery_rates, - beta_vector2, - num_points) + qt1[i] = tranch_surv_prob_gaussian(0.0, k1, num_credits, + q_vector, recovery_rates, + beta_vector1, num_points) + + qt2[i] = tranch_surv_prob_gaussian(0.0, k2, num_credits, + q_vector, recovery_rates, + beta_vector2, num_points) elif model == FinLossDistributionBuilder.LHP: - qt1[i] = tr_surv_prob_lhp( - 0.0, k1, num_credits, q_vector, recovery_rates, beta_1) + qt1[i] = tr_surv_prob_lhp(0.0, k1, num_credits, + q_vector, recovery_rates, beta_1) - qt2[i] = tr_surv_prob_lhp( - 0.0, k2, num_credits, q_vector, recovery_rates, beta_2) + qt2[i] = tr_surv_prob_lhp(0.0, k2, num_credits, + q_vector, recovery_rates, beta_2) else: raise FinError( @@ -237,8 +231,7 @@ def value_bc(self, risky_pv01 = self._cds_contract.risky_pv01( value_dt, tranche_curve)['clean_rpv01'] - mtm = self._notional * (protLegPV - upfront - - risky_pv01 * running_cpn) + mtm = self._notional * (protLegPV - upfront - risky_pv01 * running_cpn) if not self._long_protection: mtm *= -1.0 diff --git a/notebooks/products/bonds/FINBONDYIELDCURVES_FittingExample.ipynb b/notebooks/products/bonds/FINBONDYIELDCURVES_FittingExample.ipynb index 3346040b3..b1a095712 100644 --- a/notebooks/products/bonds/FINBONDYIELDCURVES_FittingExample.ipynb +++ b/notebooks/products/bonds/FINBONDYIELDCURVES_FittingExample.ipynb @@ -942,7 +942,7 @@ } ], "source": [ - "fitted_curve._curveFit._knots" + "fitted_curve._curve_fit._knots" ] }, { @@ -962,7 +962,7 @@ } ], "source": [ - "fitted_curve._curveFit._power" + "fitted_curve._curve_fit._power" ] }, { @@ -990,7 +990,7 @@ } ], "source": [ - "fitted_curve._curveFit._spline" + "fitted_curve._curve_fit._spline" ] }, { diff --git a/notebooks/products/bonds/FINBONDYIELDCURVE_FittingToBondMarketPrices.ipynb b/notebooks/products/bonds/FINBONDYIELDCURVE_FittingToBondMarketPrices.ipynb index 8a3b90eb8..77bc75296 100644 --- a/notebooks/products/bonds/FINBONDYIELDCURVE_FittingToBondMarketPrices.ipynb +++ b/notebooks/products/bonds/FINBONDYIELDCURVE_FittingToBondMarketPrices.ipynb @@ -175,7 +175,7 @@ } ], "source": [ - "print(fitted_curve1._curveFit)" + "print(fitted_curve1._curve_fit)" ] }, { @@ -237,7 +237,7 @@ } ], "source": [ - "print(fitted_curve2._curveFit)" + "print(fitted_curve2._curve_fit)" ] }, { @@ -289,7 +289,7 @@ } ], "source": [ - "print(fitted_curve3._curveFit)" + "print(fitted_curve3._curve_fit)" ] }, { @@ -343,7 +343,7 @@ } ], "source": [ - "print(fitted_curve4._curveFit)" + "print(fitted_curve4._curve_fit)" ] }, { @@ -400,7 +400,7 @@ } ], "source": [ - "print(fitted_curve5._curveFit)" + "print(fitted_curve5._curve_fit)" ] }, { diff --git a/tests/test_FinBond.py b/tests/test_FinBond.py index 3f53317f6..2afc0b5c6 100644 --- a/tests/test_FinBond.py +++ b/tests/test_FinBond.py @@ -351,16 +351,15 @@ def test_key_rate_durations_bloomberg_example(): my_rates = np.array([5.0367, 4.7327, 4.1445, 3.8575, 3.6272, 3.5825, 3.5347]) / 100.0 - key_rate_tenors, key_rate_durations =\ - bond.key_rate_durations(settle_dt, - ytm, - key_rate_tenors=my_tenors, - rates=my_rates) + krt, krd = bond.key_rate_durations(settle_dt, + ytm, + key_rate_tenors=my_tenors, + rates=my_rates) bbg_key_rate_durations = [-0.001, -.009, -0.022, 1.432, 2.527, 0.00, 0.00, 0.00, 0.00] - for i in range(len(key_rate_durations)): - assert round(key_rate_durations[i], 3) == bbg_key_rate_durations[i] + for i in range(len(krd)): + assert round(krd[i], 3) == bbg_key_rate_durations[i] ############################################################################### diff --git a/tests/test_FinBondYieldCurve.py b/tests/test_FinBondYieldCurve.py index e75d0f7bb..6d95cf7fd 100644 --- a/tests/test_FinBondYieldCurve.py +++ b/tests/test_FinBondYieldCurve.py @@ -9,11 +9,11 @@ from financepy.utils.day_count import DayCountTypes from financepy.utils.date import Date, from_datetime from financepy.products.bonds.bond import Bond -from financepy.products.bonds.yield_curve import BondYieldCurve -from financepy.products.bonds.yield_curve_model import CurveFitPolynomial -from financepy.products.bonds.yield_curve_model import CurveFitBSpline -from financepy.products.bonds.yield_curve_model import CurveFitNelsonSiegel -from financepy.products.bonds.yield_curve_model import CurveFitNelsonSiegelSvensson +from financepy.products.bonds.bond_yield_curve import BondYieldCurve +from financepy.products.bonds.curve_fits import CurveFitPolynomial +from financepy.products.bonds.curve_fits import CurveFitBSpline +from financepy.products.bonds.curve_fits import CurveFitNelsonSiegel +from financepy.products.bonds.curve_fits import CurveFitNelsonSiegelSvensson path = os.path.join(os.path.dirname(__file__), './data/giltBondPrices.txt') bondDataFrame = pd.read_csv(path, sep='\t') @@ -44,7 +44,7 @@ def test_poly(): curveFitMethod = CurveFitPolynomial(5) fitted_curve = BondYieldCurve(settlement, bonds, ylds, curveFitMethod) - coeffs = fitted_curve._curveFit._coeffs + coeffs = fitted_curve._curve_fit._coeffs assert round(coeffs[0] * 1e9, 4) == -1.4477 assert round(coeffs[1] * 1e7, 4) == 1.7840 assert round(coeffs[2] * 1e6, 4) == -7.4147 @@ -57,22 +57,22 @@ def test_nelson_siegel(): curveFitMethod = CurveFitNelsonSiegel() fitted_curve = BondYieldCurve(settlement, bonds, ylds, curveFitMethod) - assert round(fitted_curve._curveFit._beta_1, 3) == -0.094 - assert round(fitted_curve._curveFit._beta_2, 3) == 0.092 - assert round(fitted_curve._curveFit._beta_3, 3) == 0.259 - assert round(fitted_curve._curveFit._tau, 1) == 35.8 + assert round(fitted_curve._curve_fit._beta_1, 3) == -0.094 + assert round(fitted_curve._curve_fit._beta_2, 3) == 0.092 + assert round(fitted_curve._curve_fit._beta_3, 3) == 0.259 + assert round(fitted_curve._curve_fit._tau, 1) == 35.8 def test_nelson_siegel_svensson(): curveFitMethod = CurveFitNelsonSiegelSvensson() fitted_curve = BondYieldCurve(settlement, bonds, ylds, curveFitMethod) - assert round(fitted_curve._curveFit._beta_1, 4) == 0.0460 - assert round(fitted_curve._curveFit._beta_2, 4) == -0.0433 - assert round(fitted_curve._curveFit._beta_3, 4) == -0.0523 - assert round(fitted_curve._curveFit._beta_4, 4) == -0.0376 - assert round(fitted_curve._curveFit._tau_1, 3) == 3.177 - assert round(fitted_curve._curveFit._tau_2, 4) == 100.0000 + assert round(fitted_curve._curve_fit._beta_1, 4) == 0.0460 + assert round(fitted_curve._curve_fit._beta_2, 4) == -0.0433 + assert round(fitted_curve._curve_fit._beta_3, 4) == -0.0523 + assert round(fitted_curve._curve_fit._beta_4, 4) == -0.0376 + assert round(fitted_curve._curve_fit._tau_1, 3) == 3.177 + assert round(fitted_curve._curve_fit._tau_2, 4) == 100.0000 def test_interpolated_yield(): diff --git a/tests/test_FinBondZeroCurve.py b/tests/test_FinBondZeroCurve.py index a423450fb..594c6a08e 100644 --- a/tests/test_FinBondZeroCurve.py +++ b/tests/test_FinBondZeroCurve.py @@ -2,7 +2,7 @@ # Copyright (C) 2018, 2019, 2020 Dominic O'Kane ############################################################################### -from financepy.products.bonds.zero_curve import BondZeroCurve +from financepy.products.bonds.bond_zero_curve import BondZeroCurve from financepy.products.bonds.bond import Bond from financepy.utils.date import Date, from_datetime from financepy.utils.day_count import DayCountTypes diff --git a/tests/test_FinCDSTranche.py b/tests/test_FinCDSTranche.py index 2def09ae6..898375fbb 100644 --- a/tests/test_FinCDSTranche.py +++ b/tests/test_FinCDSTranche.py @@ -98,7 +98,7 @@ def test_homogeneous(): corr2, num_points, method) - assert round(v[3] * 10000, 4) == 4.3979 + assert round(v[3] * 10000, 4) == 4.5835 method = FinLossDistributionBuilder.LHP v = tranche7.value_bc( @@ -160,7 +160,7 @@ def test_heterogeneous(): corr2, num_points, method) - assert round(v[3] * 10000, 4) == 12.3681 + assert round(v[3] * 10000, 4) == 16.1785 method = FinLossDistributionBuilder.LHP v = tranche6.value_bc( diff --git a/tests_golden/TestFinBond.py b/tests_golden/TestFinBond.py index 960115246..ee074125f 100644 --- a/tests_golden/TestFinBond.py +++ b/tests_golden/TestFinBond.py @@ -620,15 +620,13 @@ def test_key_rate_durations(): dc_type, freq_type, settlementDays, exDiv, calendar = \ get_bond_market_conventions(BondMarkets.UNITED_STATES) - bond = Bond(issue_dt, maturity_dt, coupon, - freq_type, dc_type, ex_div_days) + bond = Bond(issue_dt, maturity_dt, coupon, freq_type, dc_type, ex_div_days) settle_dt = Date(24, 4, 2023) - ytm = 3.725060/100 + ytm = 3.725060 / 100.0 - key_rate_tenors, key_rate_durations = bond.key_rate_durations( - settle_dt, ytm) + krt, krd = bond.key_rate_durations(settle_dt, ytm) # print(key_rate_tenors) # print(key_rate_durations) diff --git a/tests_golden/TestFinBondYieldCurve.py b/tests_golden/TestFinBondYieldCurve.py index bfebd8829..c7083d7c8 100644 --- a/tests_golden/TestFinBondYieldCurve.py +++ b/tests_golden/TestFinBondYieldCurve.py @@ -9,11 +9,11 @@ sys.path.append("..") from FinTestCases import FinTestCases, globalTestCaseMode -from financepy.products.bonds.yield_curve_model import CurveFitNelsonSiegelSvensson -from financepy.products.bonds.yield_curve_model import CurveFitNelsonSiegel -from financepy.products.bonds.yield_curve_model import CurveFitBSpline -from financepy.products.bonds.yield_curve_model import CurveFitPolynomial -from financepy.products.bonds.yield_curve import BondYieldCurve +from financepy.products.bonds.curve_fits import CurveFitNelsonSiegelSvensson +from financepy.products.bonds.curve_fits import CurveFitNelsonSiegel +from financepy.products.bonds.curve_fits import CurveFitBSpline +from financepy.products.bonds.curve_fits import CurveFitPolynomial +from financepy.products.bonds.bond_yield_curve import BondYieldCurve from financepy.products.bonds.bond import Bond from financepy.utils.date import Date, from_datetime from financepy.utils.day_count import DayCountTypes @@ -58,47 +58,47 @@ def test_BondYieldCurve(): ############################################################################### - curveFitMethod = CurveFitPolynomial() - fitted_curve1 = BondYieldCurve(settlement, bonds, ylds, curveFitMethod) + curve_fitter = CurveFitPolynomial() + fitted_curve1 = BondYieldCurve(settlement, bonds, ylds, curve_fitter) # fitted_curve1.display("GBP Yield Curve") - curveFitMethod = CurveFitPolynomial(5) - fitted_curve2 = BondYieldCurve(settlement, bonds, ylds, curveFitMethod) + curve_fitter = CurveFitPolynomial(5) + fitted_curve2 = BondYieldCurve(settlement, bonds, ylds, curve_fitter) # fitted_curve2.display("GBP Yield Curve") - curveFitMethod = CurveFitNelsonSiegel() - fitted_curve3 = BondYieldCurve(settlement, bonds, ylds, curveFitMethod) + curve_fitter = CurveFitNelsonSiegel() + fitted_curve3 = BondYieldCurve(settlement, bonds, ylds, curve_fitter) # fitted_curve3.display("GBP Yield Curve") - curveFitMethod = CurveFitNelsonSiegelSvensson() - fitted_curve4 = BondYieldCurve(settlement, bonds, ylds, curveFitMethod) + curve_fitter = CurveFitNelsonSiegelSvensson() + fitted_curve4 = BondYieldCurve(settlement, bonds, ylds, curve_fitter) # fitted_curve4.display("GBP Yield Curve") - curveFitMethod = CurveFitBSpline() - fitted_curve5 = BondYieldCurve(settlement, bonds, ylds, curveFitMethod) + curve_fitter = CurveFitBSpline() + fitted_curve5 = BondYieldCurve(settlement, bonds, ylds, curve_fitter) # fitted_curve5.display("GBP Yield Curve") ############################################################################### test_cases.header("PARAMETER", "VALUE") - test_cases.print("values", fitted_curve1._curveFit._coeffs) + test_cases.print("values", fitted_curve1._curve_fit._coeffs) test_cases.header("PARAMETER", "VALUE") - test_cases.print("values", fitted_curve2._curveFit._coeffs) + test_cases.print("values", fitted_curve2._curve_fit._coeffs) test_cases.header("PARAMETER", "VALUE") - test_cases.print("beta_1", fitted_curve3._curveFit._beta_1) - test_cases.print("beta_2", fitted_curve3._curveFit._beta_2) - test_cases.print("beta_3", fitted_curve3._curveFit._beta_3) - test_cases.print("tau", fitted_curve3._curveFit._tau) + test_cases.print("beta_1", fitted_curve3._curve_fit._beta_1) + test_cases.print("beta_2", fitted_curve3._curve_fit._beta_2) + test_cases.print("beta_3", fitted_curve3._curve_fit._beta_3) + test_cases.print("tau", fitted_curve3._curve_fit._tau) test_cases.header("PARAMETER", "VALUE") - test_cases.print("beta_1", fitted_curve4._curveFit._beta_1) - test_cases.print("beta_2", fitted_curve4._curveFit._beta_2) - test_cases.print("beta_3", fitted_curve4._curveFit._beta_3) - test_cases.print("beta_4", fitted_curve4._curveFit._beta_4) - test_cases.print("tau_1", fitted_curve4._curveFit._tau_1) - test_cases.print("tau_2", fitted_curve4._curveFit._tau_2) + test_cases.print("beta_1", fitted_curve4._curve_fit._beta_1) + test_cases.print("beta_2", fitted_curve4._curve_fit._beta_2) + test_cases.print("beta_3", fitted_curve4._curve_fit._beta_3) + test_cases.print("beta_4", fitted_curve4._curve_fit._beta_4) + test_cases.print("tau_1", fitted_curve4._curve_fit._tau_1) + test_cases.print("tau_2", fitted_curve4._curve_fit._tau_2) ############################################################################### diff --git a/tests_golden/TestFinBondZeroCoupon.py b/tests_golden/TestFinBondZeroCoupon.py index aac6cbc75..b3538d9c6 100644 --- a/tests_golden/TestFinBondZeroCoupon.py +++ b/tests_golden/TestFinBondZeroCoupon.py @@ -10,7 +10,7 @@ from financepy.products.bonds.bond_zero import BondZero from financepy.products.bonds.bond import Bond, YTMCalcType -from financepy.products.bonds.zero_curve import BondZeroCurve +from financepy.products.bonds.bond_zero_curve import BondZeroCurve from financepy.utils.date import Date, from_datetime from financepy.utils.day_count import DayCountTypes diff --git a/tests_golden/TestFinBondZeroCurve.py b/tests_golden/TestFinBondZeroCurve.py index 34cf7d8d8..c454f258e 100644 --- a/tests_golden/TestFinBondZeroCurve.py +++ b/tests_golden/TestFinBondZeroCurve.py @@ -7,9 +7,8 @@ from FinTestCases import FinTestCases, globalTestCaseMode from financepy.products.bonds.bond import Bond -from financepy.products.bonds.bond import YTMCalcType -from financepy.products.bonds.zero_curve import BondZeroCurve +from financepy.products.bonds.bond_zero_curve import BondZeroCurve from financepy.utils.date import Date, from_datetime from financepy.utils.day_count import DayCountTypes diff --git a/tests_golden/golden/TestFinCDSTranche_GOLDEN.testLog b/tests_golden/golden/TestFinCDSTranche_GOLDEN.testLog index 618fdfbb7..b64ffd235 100644 --- a/tests_golden/golden/TestFinCDSTranche_GOLDEN.testLog +++ b/tests_golden/golden/TestFinCDSTranche_GOLDEN.testLog @@ -1,4 +1,4 @@ -File Created on:20230823_143016 +File Created on:20240219_205759 HEADER,DATE, RESULTS,01-MAR-2007, RESULTS,02-MAR-2007, @@ -10,34 +10,34 @@ HEADER,LABEL,VALUE, RESULTS,INTRINSIC SPD TRANCHE MATURITY,23.97673839, RESULTS,ADJUSTED SPD TRANCHE MATURITY,39.96123065, HEADER,METHOD,TIME,NumPoints,K1,K2,Sprd, -RESULTS,FinLossDistributionBuilder.RECURSION,0.03124452,40,0.00000000,0.03000000,582.31892962, -RESULTS,FinLossDistributionBuilder.RECURSION,0.07024550,40,0.03000000,0.06000000,105.37199077, -RESULTS,FinLossDistributionBuilder.RECURSION,0.06285286,40,0.06000000,0.09000000,29.98035069, -RESULTS,FinLossDistributionBuilder.RECURSION,0.06248784,40,0.09000000,0.12000000,8.56434100, -RESULTS,FinLossDistributionBuilder.RECURSION,0.06287479,40,0.12000000,0.22000000,4.78447761, -RESULTS,FinLossDistributionBuilder.RECURSION,0.07504249,40,0.22000000,0.60000000,0.15295482, -RESULTS,FinLossDistributionBuilder.RECURSION,0.02174544,40,0.00000000,0.60000000,39.96169270, -RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.01562095,40,0.00000000,0.03000000,582.31892962, -RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.01562214,40,0.03000000,0.06000000,105.37199077, -RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.01558948,40,0.06000000,0.09000000,29.98035069, -RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.01562119,40,0.09000000,0.12000000,8.56434100, -RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.00350761,40,0.12000000,0.22000000,4.78447761, -RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.01250339,40,0.22000000,0.60000000,0.15295482, -RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.01562285,40,0.00000000,0.60000000,39.96169270, -RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00000000,40,0.00000000,0.03000000,590.00955985, -RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.01562142,40,0.03000000,0.06000000,87.90099025, -RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00000000,40,0.06000000,0.09000000,24.46050294, -RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.01562142,40,0.09000000,0.12000000,7.89601447, -RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00000000,40,0.12000000,0.22000000,4.39794957, -RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.01562119,40,0.22000000,0.60000000,0.32539425, -RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00000000,40,0.00000000,0.60000000,38.55938087, -RESULTS,FinLossDistributionBuilder.LHP,0.01562119,40,0.00000000,0.03000000,605.05578321, -RESULTS,FinLossDistributionBuilder.LHP,0.00000000,40,0.03000000,0.06000000,94.39602471, -RESULTS,FinLossDistributionBuilder.LHP,0.00950718,40,0.06000000,0.09000000,25.56482773, -RESULTS,FinLossDistributionBuilder.LHP,0.00650334,40,0.09000000,0.12000000,6.74523985, -RESULTS,FinLossDistributionBuilder.LHP,0.00000000,40,0.12000000,0.22000000,4.16095628, -RESULTS,FinLossDistributionBuilder.LHP,0.00000000,40,0.22000000,0.60000000,0.12667946, -RESULTS,FinLossDistributionBuilder.LHP,0.00000000,40,0.00000000,0.60000000,39.96165791, +RESULTS,FinLossDistributionBuilder.RECURSION,0.01527643,40,0.00000000,0.03000000,582.31892962, +RESULTS,FinLossDistributionBuilder.RECURSION,0.01563430,40,0.03000000,0.06000000,105.37199077, +RESULTS,FinLossDistributionBuilder.RECURSION,0.01585722,40,0.06000000,0.09000000,29.98035069, +RESULTS,FinLossDistributionBuilder.RECURSION,0.03406096,40,0.09000000,0.12000000,8.56434100, +RESULTS,FinLossDistributionBuilder.RECURSION,0.02110147,40,0.12000000,0.22000000,4.78447761, +RESULTS,FinLossDistributionBuilder.RECURSION,0.02259564,40,0.22000000,0.60000000,0.15295482, +RESULTS,FinLossDistributionBuilder.RECURSION,0.01200008,40,0.00000000,0.60000000,39.96169270, +RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.00399733,40,0.00000000,0.03000000,582.31892962, +RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.00600004,40,0.03000000,0.06000000,105.37199077, +RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.00599813,40,0.06000000,0.09000000,29.98035069, +RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.00599980,40,0.09000000,0.12000000,8.56434100, +RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.00700068,40,0.12000000,0.22000000,4.78447761, +RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.00599957,40,0.22000000,0.60000000,0.15295482, +RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.00500011,40,0.00000000,0.60000000,39.96169270, +RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00399995,40,0.00000000,0.03000000,612.81954582, +RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00499988,40,0.03000000,0.06000000,104.16760547, +RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00500202,40,0.06000000,0.09000000,29.06425470, +RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00622725,40,0.09000000,0.12000000,7.81647410, +RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00500870,40,0.12000000,0.22000000,4.58346035, +RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00651646,40,0.22000000,0.60000000,0.10296223, +RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00423837,40,0.00000000,0.60000000,41.06187633, +RESULTS,FinLossDistributionBuilder.LHP,0.41559792,40,0.00000000,0.03000000,605.05578321, +RESULTS,FinLossDistributionBuilder.LHP,0.00199556,40,0.03000000,0.06000000,94.39602471, +RESULTS,FinLossDistributionBuilder.LHP,0.00199747,40,0.06000000,0.09000000,25.56482773, +RESULTS,FinLossDistributionBuilder.LHP,0.00300002,40,0.09000000,0.12000000,6.74523985, +RESULTS,FinLossDistributionBuilder.LHP,0.00300002,40,0.12000000,0.22000000,4.16095628, +RESULTS,FinLossDistributionBuilder.LHP,0.00199986,40,0.22000000,0.60000000,0.12667946, +RESULTS,FinLossDistributionBuilder.LHP,0.00300002,40,0.00000000,0.60000000,39.96165791, BANNER,=================================================================== BANNER,=================== HETEROGENEOUS CURVES ========================== BANNER,=================================================================== @@ -45,32 +45,32 @@ HEADER,LABEL,VALUE, RESULTS,INTRINSIC SPD TRANCHE MATURITY,34.33262061, RESULTS,ADJUSTED SPD TRANCHE MATURITY,57.22103434, HEADER,METHOD,TIME,NumPoints,K1,K2,Sprd, -RESULTS,FinLossDistributionBuilder.RECURSION,0.04724836,40,0.00000000,0.03000000,868.13272067, -RESULTS,FinLossDistributionBuilder.RECURSION,0.06455445,40,0.03000000,0.06000000,173.50320359, -RESULTS,FinLossDistributionBuilder.RECURSION,0.04945040,40,0.06000000,0.09000000,51.63589591, -RESULTS,FinLossDistributionBuilder.RECURSION,0.07855844,40,0.09000000,0.12000000,16.08789076, -RESULTS,FinLossDistributionBuilder.RECURSION,0.06251693,40,0.12000000,0.22000000,6.74960773, -RESULTS,FinLossDistributionBuilder.RECURSION,0.06294346,40,0.22000000,0.60000000,0.17181554, -RESULTS,FinLossDistributionBuilder.RECURSION,0.03124475,40,0.00000000,0.60000000,57.22142675, -RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.00000000,40,0.00000000,0.03000000,868.43775000, -RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.01561928,40,0.03000000,0.06000000,173.43041898, -RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.01562071,40,0.06000000,0.09000000,51.56575481, -RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.01607227,40,0.09000000,0.12000000,16.09818226, -RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.00000000,40,0.12000000,0.22000000,6.76023011, -RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.01565599,40,0.22000000,0.60000000,0.17275598, -RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.01561856,40,0.00000000,0.60000000,57.22142675, -RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00000000,40,0.00000000,0.03000000,890.60181219, -RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.01562119,40,0.03000000,0.06000000,145.96041791, -RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00000000,40,0.06000000,0.09000000,40.52312572, -RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.01562119,40,0.09000000,0.12000000,12.36809458, -RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.01562357,40,0.12000000,0.22000000,5.52092353, -RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00000000,40,0.22000000,0.60000000,0.25504581, -RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.01607347,40,0.00000000,0.60000000,55.68453818, -RESULTS,FinLossDistributionBuilder.LHP,0.00000000,40,0.00000000,0.03000000,824.95731896, -RESULTS,FinLossDistributionBuilder.LHP,0.00000000,40,0.03000000,0.06000000,160.63188393, -RESULTS,FinLossDistributionBuilder.LHP,0.00000000,40,0.06000000,0.09000000,50.23526406, -RESULTS,FinLossDistributionBuilder.LHP,0.01566863,40,0.09000000,0.12000000,15.81482847, -RESULTS,FinLossDistributionBuilder.LHP,0.00000000,40,0.12000000,0.22000000,9.22514508, -RESULTS,FinLossDistributionBuilder.LHP,0.00000000,40,0.22000000,0.60000000,0.33855124, -RESULTS,FinLossDistributionBuilder.LHP,0.01560545,40,0.00000000,0.60000000,57.22138944, +RESULTS,FinLossDistributionBuilder.RECURSION,0.01199675,40,0.00000000,0.03000000,868.13272067, +RESULTS,FinLossDistributionBuilder.RECURSION,0.02099895,40,0.03000000,0.06000000,173.50320359, +RESULTS,FinLossDistributionBuilder.RECURSION,0.02099991,40,0.06000000,0.09000000,51.63589591, +RESULTS,FinLossDistributionBuilder.RECURSION,0.02051020,40,0.09000000,0.12000000,16.08789076, +RESULTS,FinLossDistributionBuilder.RECURSION,0.02099824,40,0.12000000,0.22000000,6.74960773, +RESULTS,FinLossDistributionBuilder.RECURSION,0.02100015,40,0.22000000,0.60000000,0.17181554, +RESULTS,FinLossDistributionBuilder.RECURSION,0.01200008,40,0.00000000,0.60000000,57.22142675, +RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.00500011,40,0.00000000,0.03000000,868.43775000, +RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.00600052,40,0.03000000,0.06000000,173.43041898, +RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.00599813,40,0.06000000,0.09000000,51.56575481, +RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.00600052,40,0.09000000,0.12000000,16.09818226, +RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.00646591,40,0.12000000,0.22000000,6.76023011, +RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.00599933,40,0.22000000,0.60000000,0.17275598, +RESULTS,FinLossDistributionBuilder.ADJUSTED_BINOMIAL,0.00551486,40,0.00000000,0.60000000,57.22142675, +RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00399899,40,0.00000000,0.03000000,901.94569770, +RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00599980,40,0.03000000,0.06000000,173.10583661, +RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00500512,40,0.06000000,0.09000000,51.50094167, +RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00499892,40,0.09000000,0.12000000,16.17854879, +RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00540900,40,0.12000000,0.22000000,6.81339711, +RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00587273,40,0.22000000,0.60000000,0.19167513, +RESULTS,FinLossDistributionBuilder.GAUSSIAN,0.00399637,40,0.00000000,0.60000000,58.23030816, +RESULTS,FinLossDistributionBuilder.LHP,0.00199699,40,0.00000000,0.03000000,824.95731896, +RESULTS,FinLossDistributionBuilder.LHP,0.00299978,40,0.03000000,0.06000000,160.63188393, +RESULTS,FinLossDistributionBuilder.LHP,0.00300074,40,0.06000000,0.09000000,50.23526406, +RESULTS,FinLossDistributionBuilder.LHP,0.00299907,40,0.09000000,0.12000000,15.81482847, +RESULTS,FinLossDistributionBuilder.LHP,0.00200033,40,0.12000000,0.22000000,9.22514508, +RESULTS,FinLossDistributionBuilder.LHP,0.00199699,40,0.22000000,0.60000000,0.33855420, +RESULTS,FinLossDistributionBuilder.LHP,0.00299954,40,0.00000000,0.60000000,57.22139155, BANNER,===================================================================