Skip to content

Commit

Permalink
Repaired Notebooks
Browse files Browse the repository at this point in the history
  • Loading branch information
domokane committed Apr 30, 2024
1 parent 140b616 commit b23fd84
Show file tree
Hide file tree
Showing 155 changed files with 17,242 additions and 4,794 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.350') + " - This build: 10 Mar 2024 at 18:42 #" + cr
s += "# FINANCEPY BETA Version " + str('0.350') + " - This build: 30 Apr 2024 at 22:35 #" + 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
12 changes: 6 additions & 6 deletions financepy/market/curves/discount_curve.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,13 @@ def __init__(self,
raise FinError("Times are not sorted in increasing order")

self.value_dt = value_dt
self.interp_type = interp_type
self.freq_type = FrequencyTypes.CONTINUOUS
# This needs to be thought about - I just assign an arbitrary value
self.dc_type = DayCountTypes.ACT_ACT_ISDA

self._dfs = np.array(self._dfs)
self._interpolator = Interpolator(self.interp_type)
self._interp_type = interp_type
self._interpolator = Interpolator(self._interp_type)
self._interpolator.fit(self._times, self._dfs)

###########################################################################
Expand Down Expand Up @@ -304,14 +304,14 @@ def _df(self,
""" Hidden function to calculate a discount factor from a time or a
vector of times. Discourage usage in favour of passing in dates. """

if self.interp_type is InterpTypes.FLAT_FWD_RATES or \
self.interp_type is InterpTypes.LINEAR_ZERO_RATES or \
self.interp_type is InterpTypes.LINEAR_FWD_RATES:
if self._interp_type is InterpTypes.FLAT_FWD_RATES or \
self._interp_type is InterpTypes.LINEAR_ZERO_RATES or \
self._interp_type is InterpTypes.LINEAR_FWD_RATES:

df = interpolate(t,
self._times,
self._dfs,
self.interp_type.value)
self._interp_type.value)

else:

Expand Down
2 changes: 1 addition & 1 deletion financepy/market/curves/discount_curve_flat.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def __init__(self,
self.dc_type = dc_type

# This is used by some inherited functions, so we choose the simplest
self.interp_type = InterpTypes.FLAT_FWD_RATES
self._interp_type = InterpTypes.FLAT_FWD_RATES

# Need to set up a grid of times and discount factors
years = np.linspace(0.0, 10.0, 41)
Expand Down
9 changes: 5 additions & 4 deletions financepy/market/curves/discount_curve_zeros.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ def __init__(self,
self.value_dt = value_dt
self.freq_type = freq_type
self.dc_type = dc_type
self.interp_type = interp_type

self._zero_rates = np.array(zero_rates)
self._zero_dts = zero_dts
Expand All @@ -82,7 +81,9 @@ def __init__(self,
self.dc_type)

self._dfs = np.array(dfs)
self._interpolator = Interpolator(self.interp_type)

self._interp_type = interp_type
self._interpolator = Interpolator(self._interp_type)
self._interpolator.fit(self._times, self._dfs)

# ###############################################################################
Expand Down Expand Up @@ -112,10 +113,10 @@ def __repr__(self):
s += label_to_string("VALUATION DATE", self.value_dt)
s += label_to_string("FREQUENCY TYPE", (self.freq_type))
s += label_to_string("DAY COUNT TYPE", (self.dc_type))
s += label_to_string("INTERP TYPE", (self.interp_type))
s += label_to_string("INTERP TYPE", (self._interp_type))

s += label_to_string("DATES", "ZERO RATES")
num_points = len(self.times)
num_points = len(self._times)
for i in range(0, num_points):
s += label_to_string("%12s" % self._zero_dts[i],
"%10.7f" % self._zero_rates[i])
Expand Down
44 changes: 22 additions & 22 deletions financepy/models/hw_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,8 @@ def american_bond_option_tree_fast(t_exp,
_dt,
_tree_times,
_df_times, _df_values):
""" Value an option on a bond with coupons that can have European or
American exercise. Some minor issues to do with handling coupons on
""" Value an option on a bond with cpns that can have European or
American exercise. Some minor issues to do with handling cpns on
the option expiry date need to be solved. """

DEBUG = False
Expand All @@ -195,13 +195,13 @@ def american_bond_option_tree_fast(t_exp,

###########################################################################

# Want to add coupons before expiry to the grid so that we can value
# Want to add cpns before expiry to the grid so that we can value
# their impact on the decision to exercise the option early
tree_flows = np.zeros(num_time_steps)
num_cpns = len(cpn_times)

# Flows that fall on the expiry date included. The tree only goes out to
# the expiry date so coupons after this date do not go onto the tree.
# the expiry date so cpns after this date do not go onto the tree.
for i in range(0, num_cpns):
t_cpn = cpn_times[i]
if t_cpn <= t_exp:
Expand All @@ -217,7 +217,7 @@ def american_bond_option_tree_fast(t_exp,
# result in some convergence noise issues as it is inconsistent
###########################################################################

# I star_t the tree with the previous coupon time and amount
# I star_t the tree with the previous cpn time and amount
# (does not matter)
mapped_times = np.zeros(0) # CHANGE
mapped_amounts = np.zeros(0) # CHANGE
Expand Down Expand Up @@ -417,7 +417,7 @@ def bermudan_swaption_tree_fast(t_exp, t_mat, strike_price, face_amount,
exercise_typeInt,
_df_times, _df_values,
_tree_times, _Q, _pu, _pm, _pd, _r_t, _dt, _a):
""" Option to enter into a swap that can be exercised on coupon payment
""" Option to enter into a swap that can be exercised on cpn payment
dates after the star_t of the exercise period. Due to multiple exercise
times we need to extend tree out to bond maturity and take into account
cash flows through time. """
Expand Down Expand Up @@ -607,8 +607,8 @@ def callable_puttable_bond_tree_fast(cpn_times, cpn_flows,
_sigma, _a, _Q, # IS SIGMA USED ?
_pu, _pm, _pd, _r_t, _dt, _tree_times,
_df_times, _df_values):
""" Value an option on a bond with coupons that can have European or
American exercise. Some minor issues to do with handling coupons on
""" Value an option on a bond with cpns that can have European or
American exercise. Some minor issues to do with handling cpns on
the option expiry date need to be solved. """

# print("Coupon Times:", cpn_times)
Expand All @@ -618,7 +618,7 @@ def callable_puttable_bond_tree_fast(cpn_times, cpn_flows,
# print("DF Values:", _df_values)

if np.any(cpn_times < 0.0):
raise FinError("No coupon times can be before the value date.")
raise FinError("No cpn times can be before the value date.")

num_time_steps, num_nodes = _Q.shape
dt = _dt
Expand All @@ -627,7 +627,7 @@ def callable_puttable_bond_tree_fast(cpn_times, cpn_flows,
maturity_step = int(t_mat/dt + 0.50)

###########################################################################
# Map coupons onto tree while preserving their present value
# Map cpns onto tree while preserving their present value
###########################################################################

tree_flows = np.zeros(num_time_steps)
Expand Down Expand Up @@ -772,7 +772,7 @@ def callable_puttable_bond_tree_fast(cpn_times, cpn_flows,
vd = call_put_bond_values[m+1, kN-1]

vhold = (pu*vu + pm*vm + pd*vd) * df
# Need to make add on coupons paid if we hold
# Need to make add on cpns paid if we hold
vhold = vhold + flow
value = min(max(vhold - accrued[m], vput), vcall) + accrued[m]
call_put_bond_values[m, kN] = value
Expand All @@ -784,7 +784,7 @@ def callable_puttable_bond_tree_fast(cpn_times, cpn_flows,


def fwd_dirty_bond_price(r_t, *args):
""" Price a coupon bearing bond on the option expiry date and return
""" Price a cpn bearing bond on the option expiry date and return
the difference from a strike price. This is used in a root search to
find the future expiry time short rate that makes the bond price equal
to the option strike price. It is a key step in the Jamshidian bond
Expand Down Expand Up @@ -884,7 +884,7 @@ def option_on_zcb(self,
t_exp, t_mat,
strike, face_amount,
df_times, df_values):
""" Price an option on a zero coupon bond using analytical solution of
""" Price an option on a zero cpn bond using analytical solution of
Hull-White model. User provides bond face and option strike and expiry
date and maturity date. """

Expand Down Expand Up @@ -929,7 +929,7 @@ def european_bond_option_jamshidian(self,
df_times,
df_values):
""" Valuation of a European bond option using the Jamshidian
deconstruction of the bond into a strip of zero coupon bonds with the
deconstruction of the bond into a strip of zero cpn bonds with the
short rate that would make the bond option be at the money forward. """

# print(df_times)
Expand All @@ -947,7 +947,7 @@ def european_bond_option_jamshidian(self,
args=argtuple, tol=1e-10, maxiter=50,
fprime2=None)

# Now we price a series of zero coupon bonds using this short rate
# Now we price a series of zero cpn bonds using this short rate
dt = 1e-6

pt_exp = _uinterpolate(t_exp, df_times, df_values, INTERP)
Expand All @@ -962,7 +962,7 @@ def european_bond_option_jamshidian(self,
t_cpn = cpn_times[i]
cpn = cpn_amounts[i]

if t_cpn >= t_exp: # coupons on the expiry date are included
if t_cpn >= t_exp: # cpns on the expiry date are included

pt_cpn = _uinterpolate(t_cpn, df_times, df_values, INTERP)

Expand Down Expand Up @@ -991,9 +991,9 @@ def european_bond_option_expiry_only(self,
face_amount,
cpn_times,
cpn_amounts):
""" Price a European option on a coupon-paying bond using a tree to
""" Price a European option on a cpn-paying bond using a tree to
generate short rates at the expiry date and then to use the analytical
solution of zero coupon bond prices in the HW model to calculate the
solution of zero cpn bond prices in the HW model to calculate the
corresponding bond price. User provides bond object and option details.
"""

Expand Down Expand Up @@ -1057,12 +1057,12 @@ def european_bond_option_expiry_only(self,

###############################################################################

def option_on_zero_coupon_bond_tree(self,
def option_on_zero_cpn_bond_tree(self,
t_exp,
t_mat,
strike_price,
face_amount):
""" Price an option on a zero coupon bond using a HW trinomial
""" Price an option on a zero cpn bond using a HW trinomial
tree. The discount curve was already supplied to the tree build. """

if t_exp > t_mat:
Expand Down Expand Up @@ -1218,8 +1218,8 @@ def callable_puttable_bond_tree(self,
put_times,
put_prices,
face_amount):
""" Value an option on a bond with coupons that can have European or
American exercise. Some minor issues to do with handling coupons on
""" Value an option on a bond with cpns that can have European or
American exercise. Some minor issues to do with handling cpns on
the option expiry date need to be solved. Also this function should be
moved out of the class so it can be sped up using NUMBA. """

Expand Down
44 changes: 21 additions & 23 deletions financepy/products/bonds/bond_frn.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ def __init__(self,
self.cal_type = cal_type

self.settle_dt = Date(1, 1, 1900)
self.accrued_int= None
self.accrued = None
self.accrued_days = 0.0
self.accrual_factor = 0.0

self.cpn_dts = []
self.flow_amounts = []
Expand All @@ -88,11 +89,11 @@ def _calculate_cpn_dts(self):
dg_type = DateGenRuleTypes.BACKWARD

self.cpn_dts = Schedule(self.issue_dt,
self.maturity_dt,
self.freq_type,
self.cal_type,
bd_type,
dg_type).generate()
self.maturity_dt,
self.freq_type,
self.cal_type,
bd_type,
dg_type).generate()

###########################################################################

Expand All @@ -109,15 +110,14 @@ def dirty_price_from_dm(self,
is the level of subsequent future Ibor payments and the discount
margin. """

self.accrued_interest(settle_dt, next_cpn, 1.0)

day_counter = DayCount(self.dc_type)

q = self.quoted_margin
num_flows = len(self.cpn_dts)

# We discount using Libor over the period from settlement to the ncd
(alpha, _, _) = day_counter.year_frac(settle_dt, self.ncd)

df = 1.0 / (1.0 + alpha * (current_ibor + dm))

# A full coupon is paid
Expand All @@ -128,6 +128,7 @@ def dirty_price_from_dm(self,
for i_flow in range(1, num_flows):

if self.cpn_dts[i_flow] > self.ncd:

pcd = self.cpn_dts[i_flow - 1]
ncd = self.cpn_dts[i_flow]
(alpha, _, _) = day_counter.year_frac(pcd, ncd)
Expand Down Expand Up @@ -159,8 +160,8 @@ def principal(self,
future_ibor,
dm)

accrued = self.accrued_int
principal = dirty_price * face / self.par - accrued
self.accrued = self.accrual_factor * next_cpn * 1.0
principal = dirty_price * face / self.par - self.accrued
return principal

###########################################################################
Expand Down Expand Up @@ -204,7 +205,6 @@ def dollar_credit_duration(self,
if dm > 10.0:
raise FinError("Discount margin exceeds 100000bp")

self.accrued_interest(settle_dt, next_cpn, 1.0)
dy = 0.0001

p0 = self.dirty_price_from_dm(settle_dt,
Expand Down Expand Up @@ -368,10 +368,11 @@ def clean_price_from_dm(self,
future_ibor,
dm)

accrued = self.accrued_interest(settle_dt, next_cpn, 1.0)
accrued = accrued * self.par
self.accrued_interest(settle_dt, next_cpn)

clean_price = dirty_price - accrued
self.accrued = self.accrual_factor * next_cpn * self.par

clean_price = dirty_price - self.accrued
return clean_price

###########################################################################
Expand All @@ -385,12 +386,12 @@ def discount_margin(self,
""" Calculate the bond's yield to maturity by solving the price
yield relationship using a one-dimensional root solver. """

self.accrued_interest(settle_dt, next_cpn, 1.0)
self.accrued_interest(settle_dt, next_cpn)

# Needs to be adjusted to par notional
accrued = self.accrued_int* self.par
# Accrued needs to be adjusted to par notional
self.accrued = self.accrual_factor * next_cpn * self.par

dirty_price = clean_price + accrued
dirty_price = clean_price + self.accrued

argtuple = (self, settle_dt, next_cpn, current_ibor,
future_ibor, dirty_price)
Expand All @@ -409,8 +410,7 @@ def discount_margin(self,

def accrued_interest(self,
settle_dt: Date,
next_cpn: float,
face: (float)):
next_cpn: float):
""" Calculate the amount of coupon that has accrued between the
previous coupon date and the settlement date. Ex-dividend dates are
not handled. Contact me if you need this functionality. """
Expand All @@ -434,10 +434,8 @@ def accrued_interest(self,
self.freq_type)

self.alpha = 1.0 - acc_factor * self.freq

self.accrued_int= acc_factor * face * next_cpn
self.accrual_factor = acc_factor
self.accrued_days = num
return self.accrued_interest

###########################################################################

Expand Down
Loading

0 comments on commit b23fd84

Please sign in to comment.