PYield 0.9.4__tar.gz → 0.9.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyield-0.9.4 → pyield-0.9.6}/PKG-INFO +22 -3
- {pyield-0.9.4 → pyield-0.9.6}/README.md +21 -2
- pyield-0.9.6/pyield/__about__.py +1 -0
- {pyield-0.9.4 → pyield-0.9.6}/pyield/__init__.py +2 -2
- {pyield-0.9.4 → pyield-0.9.6}/pyield/fetchers/anbima.py +3 -3
- {pyield-0.9.4 → pyield-0.9.6}/pyield/fetchers/futures/intraday.py +3 -2
- pyield-0.9.6/pyield/interpolator.py +138 -0
- {pyield-0.9.4 → pyield-0.9.6}/pyield/ntnb.py +37 -54
- {pyield-0.9.4 → pyield-0.9.6}/pyield/spreads.py +1 -1
- {pyield-0.9.4 → pyield-0.9.6}/pyproject.toml +1 -1
- pyield-0.9.4/pyield/__about__.py +0 -1
- pyield-0.9.4/pyield/interpolators.py +0 -91
- {pyield-0.9.4 → pyield-0.9.6}/LICENSE +0 -0
- {pyield-0.9.4 → pyield-0.9.6}/pyield/bday.py +0 -0
- {pyield-0.9.4 → pyield-0.9.6}/pyield/date_validator.py +0 -0
- {pyield-0.9.4 → pyield-0.9.6}/pyield/fetchers/__init__.py +0 -0
- {pyield-0.9.4 → pyield-0.9.6}/pyield/fetchers/futures/__init__.py +0 -0
- {pyield-0.9.4 → pyield-0.9.6}/pyield/fetchers/futures/common.py +0 -0
- {pyield-0.9.4 → pyield-0.9.6}/pyield/fetchers/futures/historical.py +0 -0
- {pyield-0.9.4 → pyield-0.9.6}/pyield/fetchers/futures/xml.py +0 -0
- {pyield-0.9.4 → pyield-0.9.6}/pyield/fetchers/indicators.py +0 -0
- {pyield-0.9.4 → pyield-0.9.6}/pyield/fetchers/projections.py +0 -0
- {pyield-0.9.4 → pyield-0.9.6}/pyield/holidays/__init__.py +0 -0
- {pyield-0.9.4 → pyield-0.9.6}/pyield/holidays/br_holidays_new.txt +0 -0
- {pyield-0.9.4 → pyield-0.9.6}/pyield/holidays/br_holidays_old.txt +0 -0
- {pyield-0.9.4 → pyield-0.9.6}/pyield/holidays/holidays_manager.py +0 -0
- {pyield-0.9.4 → pyield-0.9.6}/pyield/py.typed +0 -0
- {pyield-0.9.4 → pyield-0.9.6}/tests/__init__.py +0 -0
- {pyield-0.9.4 → pyield-0.9.6}/tests/data/SPRD231222.zip +0 -0
- {pyield-0.9.4 → pyield-0.9.6}/tests/data/SPRD240426.zip +0 -0
- {pyield-0.9.4 → pyield-0.9.6}/tests/test_bday.py +0 -0
- {pyield-0.9.4 → pyield-0.9.6}/tests/test_futures.py +0 -0
- {pyield-0.9.4 → pyield-0.9.6}/tests/test_futures_xml.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PYield
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.6
|
|
4
4
|
Summary: A Python library for analysis of fixed income instruments in Brazil
|
|
5
5
|
Keywords: fixed-income, brazil, finance, analysis, bonds
|
|
6
6
|
Author-Email: Carlos Carvalho <cr.cj@outlook.com>
|
|
@@ -85,7 +85,7 @@ Timestamp('2024-01-02 00:00:00')
|
|
|
85
85
|
>>> yd.bday.offset(dates="30-12-2023", offset=0)
|
|
86
86
|
Timestamp('2024-01-02 00:00:00')
|
|
87
87
|
|
|
88
|
-
# Since
|
|
88
|
+
# Since 29-12-2023 is a business day, it returns the same date (offset=0)
|
|
89
89
|
>>> yd.bday.offset(dates="29-12-2023", offset=0)
|
|
90
90
|
Timestamp('2023-12-29 00:00:00')
|
|
91
91
|
|
|
@@ -127,7 +127,7 @@ TradeDate TickerSymbol ExpirationDate BDaysToExp ... LastRate LastAskRate LastB
|
|
|
127
127
|
# Fetch a DataFrame with the NTN-B data from ANBIMA
|
|
128
128
|
# Anbima data is available for the last 5 working days
|
|
129
129
|
# Obs: Anbima members have access to the full history
|
|
130
|
-
>>> yd.anbima(
|
|
130
|
+
>>> yd.anbima(bond_type='NTN-B', reference_date='12-04-2024')
|
|
131
131
|
|
|
132
132
|
BondType ReferenceDate MaturityDate BidRate AskRate IndicativeRate Price
|
|
133
133
|
NTN-B 2024-04-12 2024-08-15 0.07540 0.07504 0.07523 4,271.43565
|
|
@@ -184,3 +184,22 @@ IndicatorProjection(
|
|
|
184
184
|
0.0035 # 0.35%
|
|
185
185
|
```
|
|
186
186
|
|
|
187
|
+
### Interpolation Tools
|
|
188
|
+
```python
|
|
189
|
+
|
|
190
|
+
# Interpolate interest rates for specific business days using the Interpolator class.
|
|
191
|
+
|
|
192
|
+
# Initialize the Interpolator with known business days and interest rates.
|
|
193
|
+
>>> known_bdays = [30, 60, 90]
|
|
194
|
+
>>> known_rates = [0.045, 0.05, 0.055]
|
|
195
|
+
>>> linear_itp = yd.Interpolator("linear", known_bdays, known_rates)
|
|
196
|
+
|
|
197
|
+
# Interpolate the interest rate for a given number of business days.
|
|
198
|
+
>>> linear_itp.interpolate(45)
|
|
199
|
+
0.0475
|
|
200
|
+
|
|
201
|
+
# Use the flat forward method for interpolation.
|
|
202
|
+
>>> ff_itp = yd.Interpolator("flat_forward", known_bdays, known_rates)
|
|
203
|
+
>>> ff_itp.interpolate(45)
|
|
204
|
+
0.04833068080970859
|
|
205
|
+
```
|
|
@@ -42,7 +42,7 @@ Timestamp('2024-01-02 00:00:00')
|
|
|
42
42
|
>>> yd.bday.offset(dates="30-12-2023", offset=0)
|
|
43
43
|
Timestamp('2024-01-02 00:00:00')
|
|
44
44
|
|
|
45
|
-
# Since
|
|
45
|
+
# Since 29-12-2023 is a business day, it returns the same date (offset=0)
|
|
46
46
|
>>> yd.bday.offset(dates="29-12-2023", offset=0)
|
|
47
47
|
Timestamp('2023-12-29 00:00:00')
|
|
48
48
|
|
|
@@ -84,7 +84,7 @@ TradeDate TickerSymbol ExpirationDate BDaysToExp ... LastRate LastAskRate LastB
|
|
|
84
84
|
# Fetch a DataFrame with the NTN-B data from ANBIMA
|
|
85
85
|
# Anbima data is available for the last 5 working days
|
|
86
86
|
# Obs: Anbima members have access to the full history
|
|
87
|
-
>>> yd.anbima(
|
|
87
|
+
>>> yd.anbima(bond_type='NTN-B', reference_date='12-04-2024')
|
|
88
88
|
|
|
89
89
|
BondType ReferenceDate MaturityDate BidRate AskRate IndicativeRate Price
|
|
90
90
|
NTN-B 2024-04-12 2024-08-15 0.07540 0.07504 0.07523 4,271.43565
|
|
@@ -141,3 +141,22 @@ IndicatorProjection(
|
|
|
141
141
|
0.0035 # 0.35%
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
+
### Interpolation Tools
|
|
145
|
+
```python
|
|
146
|
+
|
|
147
|
+
# Interpolate interest rates for specific business days using the Interpolator class.
|
|
148
|
+
|
|
149
|
+
# Initialize the Interpolator with known business days and interest rates.
|
|
150
|
+
>>> known_bdays = [30, 60, 90]
|
|
151
|
+
>>> known_rates = [0.045, 0.05, 0.055]
|
|
152
|
+
>>> linear_itp = yd.Interpolator("linear", known_bdays, known_rates)
|
|
153
|
+
|
|
154
|
+
# Interpolate the interest rate for a given number of business days.
|
|
155
|
+
>>> linear_itp.interpolate(45)
|
|
156
|
+
0.0475
|
|
157
|
+
|
|
158
|
+
# Use the flat forward method for interpolation.
|
|
159
|
+
>>> ff_itp = yd.Interpolator("flat_forward", known_bdays, known_rates)
|
|
160
|
+
>>> ff_itp.interpolate(45)
|
|
161
|
+
0.04833068080970859
|
|
162
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.9.6"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from . import bday,
|
|
1
|
+
from . import bday, interpolator, ntnb
|
|
2
2
|
from .__about__ import __version__
|
|
3
3
|
from .fetchers.anbima import anbima
|
|
4
4
|
from .fetchers.futures import futures
|
|
@@ -15,6 +15,6 @@ __all__ = [
|
|
|
15
15
|
"projection",
|
|
16
16
|
"spread",
|
|
17
17
|
"futures",
|
|
18
|
-
"
|
|
18
|
+
"interpolator",
|
|
19
19
|
"spread",
|
|
20
20
|
]
|
|
@@ -90,8 +90,8 @@ def _process_raw_df(df_raw: pd.DataFrame) -> pd.DataFrame:
|
|
|
90
90
|
|
|
91
91
|
|
|
92
92
|
def anbima(
|
|
93
|
-
reference_date: str | pd.Timestamp | None = None,
|
|
94
93
|
bond_type: str | list[str] | None = None,
|
|
94
|
+
reference_date: str | pd.Timestamp | None = None,
|
|
95
95
|
) -> pd.DataFrame:
|
|
96
96
|
"""
|
|
97
97
|
Fetches indicative treasury rates from ANBIMA for a specified reference date.
|
|
@@ -100,10 +100,10 @@ def anbima(
|
|
|
100
100
|
ANBIMA, processing them into a structured pandas DataFrame.
|
|
101
101
|
|
|
102
102
|
Args:
|
|
103
|
+
bond_type (str, optional): The type of bond to filter by. Defaults to None.
|
|
103
104
|
reference_date (str | pd.Timestamp | None, optional): The date for which to
|
|
104
105
|
fetch the indicative rates. If a string is provided, it should be in the
|
|
105
106
|
format 'dd-mm-yyyy'. Defaults last business day if None.
|
|
106
|
-
bond_type (str, optional): The type of bond to filter by. Defaults to None.
|
|
107
107
|
|
|
108
108
|
Returns:
|
|
109
109
|
pd.DataFrame: A DataFrame containing the processed indicative rates for the
|
|
@@ -117,7 +117,7 @@ def anbima(
|
|
|
117
117
|
# Fetch ANBIMA data for all bonds using the last business day available
|
|
118
118
|
>>> yd.anbima("18-06-2024")
|
|
119
119
|
# Fetch ANBIMA data for NTN-B bonds using a specific reference date
|
|
120
|
-
>>> yd.anbima("
|
|
120
|
+
>>> yd.anbima("NTN-B", "18-06-2024")
|
|
121
121
|
"""
|
|
122
122
|
# Normalize the reference date
|
|
123
123
|
normalized_date = dv.normalize_date(reference_date)
|
|
@@ -167,8 +167,9 @@ def fetch_intraday_df(future_code: str) -> pd.DataFrame:
|
|
|
167
167
|
pd.DataFrame: A Pandas pd.DataFrame containing the latest DI futures data.
|
|
168
168
|
"""
|
|
169
169
|
raw_df = _fetch_b3_df(future_code)
|
|
170
|
-
if raw_df.
|
|
171
|
-
|
|
170
|
+
if "buyOffer" not in raw_df.columns:
|
|
171
|
+
# Return an empty DataFrame if the data is not available
|
|
172
|
+
return pd.DataFrame()
|
|
172
173
|
df = _process_df(raw_df)
|
|
173
174
|
df = _select_and_reorder_columns(df)
|
|
174
175
|
return df
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import bisect
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Interpolator:
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
method: Literal["flat_forward", "linear"],
|
|
12
|
+
known_bdays: pd.Series | list,
|
|
13
|
+
known_rates: pd.Series | list,
|
|
14
|
+
):
|
|
15
|
+
"""
|
|
16
|
+
Initialize the Interpolator with given atributes.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
method (Literal["flat_forward", "linear"]): Interpolation method.
|
|
20
|
+
known_bdays (pd.Series): Series of known business days.
|
|
21
|
+
known_rates (pd.Series): Series of known interest rates.
|
|
22
|
+
|
|
23
|
+
Raises:
|
|
24
|
+
ValueError: If known_bdays and known_rates do not have the same length.
|
|
25
|
+
ValueError: If the interpolation method is not recognized
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Interpolator: An instance of the Interpolator
|
|
29
|
+
|
|
30
|
+
Examples:
|
|
31
|
+
>>> known_bdays = [30, 60, 90]
|
|
32
|
+
>>> known_rates = [0.045, 0.05, 0.055]
|
|
33
|
+
>>> interpolator = Interpolator("linear", known_bdays, known_rates)
|
|
34
|
+
"""
|
|
35
|
+
self.known_bdays, self.known_rates = self._process_known_data(
|
|
36
|
+
known_bdays, known_rates
|
|
37
|
+
)
|
|
38
|
+
self.method = self._validate_method(method)
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def _validate_method(method: str) -> str:
|
|
42
|
+
"""
|
|
43
|
+
Validate the interpolation method.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
method (str): Interpolation method to validate.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
str: Validated interpolation method.
|
|
50
|
+
"""
|
|
51
|
+
valid_methods = ["flat_forward", "linear"]
|
|
52
|
+
if method not in valid_methods:
|
|
53
|
+
raise ValueError(f"Unknown interpolation method: {method}.")
|
|
54
|
+
return method
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def _process_known_data(
|
|
58
|
+
known_bdays: pd.Series | list, known_rates: pd.Series | list
|
|
59
|
+
) -> tuple[list, list]:
|
|
60
|
+
"""
|
|
61
|
+
Process and validate known business days and interest rates.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
known_bdays (pd.Series): Series of known business days.
|
|
65
|
+
known_rates (pd.Series): Series of known interest rates.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
tuple: Processed lists of business days and interest rates.
|
|
69
|
+
"""
|
|
70
|
+
if len(known_bdays) != len(known_rates):
|
|
71
|
+
raise ValueError("known_bdays and known_rates must have the same length.")
|
|
72
|
+
|
|
73
|
+
df = pd.DataFrame({"bday": known_bdays, "rate": known_rates})
|
|
74
|
+
df = df.dropna().drop_duplicates(subset="bday").sort_values("bday")
|
|
75
|
+
|
|
76
|
+
return df["bday"].to_list(), df["rate"].to_list()
|
|
77
|
+
|
|
78
|
+
def _flat_forward(self, bday: int) -> float:
|
|
79
|
+
"""Performs the interest rate interpolation using the flat forward method."""
|
|
80
|
+
|
|
81
|
+
# Find i such that known_bdays[i-1] < bday < known_bdays[i]
|
|
82
|
+
i = bisect.bisect_left(self.known_bdays, bday)
|
|
83
|
+
|
|
84
|
+
# Get previous and next known rates and business days
|
|
85
|
+
prev_rate = self.known_rates[i - 1]
|
|
86
|
+
prev_bday = self.known_bdays[i - 1]
|
|
87
|
+
next_rate = self.known_rates[i]
|
|
88
|
+
next_bday = self.known_bdays[i]
|
|
89
|
+
|
|
90
|
+
# Perform flat forward interpolation
|
|
91
|
+
a = (1 + prev_rate) ** (prev_bday / 252)
|
|
92
|
+
b = (1 + next_rate) ** (next_bday / 252)
|
|
93
|
+
c = (bday - prev_bday) / (next_bday - prev_bday)
|
|
94
|
+
return (a * (b / a) ** c) ** (252 / bday) - 1
|
|
95
|
+
|
|
96
|
+
def _linear(self, bday: int) -> float:
|
|
97
|
+
"""Performs linear interpolation."""
|
|
98
|
+
np_float = np.interp(bday, self.known_bdays, self.known_rates)
|
|
99
|
+
return float(np_float)
|
|
100
|
+
|
|
101
|
+
def interpolate(self, bday: int) -> float:
|
|
102
|
+
"""
|
|
103
|
+
Finds the appropriate interpolation point and returns the interest rate
|
|
104
|
+
interpolated by the specified method from that point.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
bday (int): Number of business days for which the interest rate is to be
|
|
108
|
+
calculated.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
float: The interest rate interpolated by the specified method for the given
|
|
112
|
+
number of business days.
|
|
113
|
+
|
|
114
|
+
Examples:
|
|
115
|
+
>>> known_bdays = [30, 60, 90]
|
|
116
|
+
>>> known_rates = [0.045, 0.05, 0.055]
|
|
117
|
+
>>> linear = Interpolator("linear", known_bdays, known_rates)
|
|
118
|
+
>>> linear.interpolate(45)
|
|
119
|
+
0.0475
|
|
120
|
+
>>> ffwd = Interpolator("flat_forward", known_bdays, known_rates)
|
|
121
|
+
>>> ffwd.interpolate(45)
|
|
122
|
+
0.04833068080970859
|
|
123
|
+
"""
|
|
124
|
+
known_bdays = self.known_bdays
|
|
125
|
+
known_rates = self.known_rates
|
|
126
|
+
|
|
127
|
+
# Special cases
|
|
128
|
+
if bday < known_bdays[0]:
|
|
129
|
+
return known_rates[0]
|
|
130
|
+
elif bday > known_bdays[-1]:
|
|
131
|
+
return known_rates[-1]
|
|
132
|
+
elif bday in known_bdays:
|
|
133
|
+
return known_rates[known_bdays.index(bday)]
|
|
134
|
+
|
|
135
|
+
if self.method == "flat_forward":
|
|
136
|
+
return self._flat_forward(bday)
|
|
137
|
+
elif self.method == "linear":
|
|
138
|
+
return self._linear(bday)
|
|
@@ -3,9 +3,9 @@ import pandas as pd
|
|
|
3
3
|
|
|
4
4
|
from . import bday
|
|
5
5
|
from . import date_validator as dv
|
|
6
|
-
from . import interpolators as ip
|
|
7
6
|
from .fetchers.anbima import anbima
|
|
8
7
|
from .fetchers.futures import futures
|
|
8
|
+
from .interpolator import Interpolator
|
|
9
9
|
from .spreads import spread
|
|
10
10
|
|
|
11
11
|
# 6% per year compounded semi-annually and rounded to 8 decimal places
|
|
@@ -35,7 +35,7 @@ def ytm_rates(reference_date: str | pd.Timestamp) -> pd.DataFrame:
|
|
|
35
35
|
Returns:
|
|
36
36
|
pd.DataFrame: A DataFrame containing the maturity dates and corresponding rates.
|
|
37
37
|
"""
|
|
38
|
-
df = anbima(
|
|
38
|
+
df = anbima("NTN-B", reference_date)
|
|
39
39
|
|
|
40
40
|
# Keep only the relevant columns for the output
|
|
41
41
|
keep_columns = ["ReferenceDate", "BondType", "MaturityDate", "IndicativeRate"]
|
|
@@ -188,30 +188,6 @@ def quotation(
|
|
|
188
188
|
return _truncate(dcf.sum(), 4)
|
|
189
189
|
|
|
190
190
|
|
|
191
|
-
def _prepare_interpolation_data(
|
|
192
|
-
reference_date: pd.Timestamp, maturity_dates: pd.Series, rates: pd.Series
|
|
193
|
-
) -> tuple:
|
|
194
|
-
"""
|
|
195
|
-
Prepare the data needed for interpolation by sorting the YTM rates by the number of
|
|
196
|
-
business days.
|
|
197
|
-
|
|
198
|
-
Args:
|
|
199
|
-
reference_date (str | pd.Timestamp): The reference date for calculating the
|
|
200
|
-
number of business days.
|
|
201
|
-
maturity_dates (pd.Series): Series of maturity dates for the bonds.
|
|
202
|
-
rates (pd.Series): Series of rates corresponding to the maturity dates.
|
|
203
|
-
|
|
204
|
-
Returns:
|
|
205
|
-
tuple: Two lists containing the ordered business days and YTM rates.
|
|
206
|
-
"""
|
|
207
|
-
bdays = bday.count(reference_date, maturity_dates)
|
|
208
|
-
df = pd.DataFrame({"BDays": bdays, "Rates": rates})
|
|
209
|
-
df.sort_values(by="BDays", ignore_index=True, inplace=True)
|
|
210
|
-
ordered_bdays = df["BDays"].to_list()
|
|
211
|
-
ordered_rates = df["Rates"].to_list()
|
|
212
|
-
return ordered_bdays, ordered_rates
|
|
213
|
-
|
|
214
|
-
|
|
215
191
|
def spot_rates(
|
|
216
192
|
settlement_date: str | pd.Timestamp,
|
|
217
193
|
maturity_dates: pd.Series,
|
|
@@ -223,37 +199,44 @@ def spot_rates(
|
|
|
223
199
|
|
|
224
200
|
Args:
|
|
225
201
|
settlement_date (str | pd.Timestamp): The reference date for settlement.
|
|
226
|
-
maturity_dates (pd.Series): Series of maturity dates for the bonds.
|
|
227
|
-
|
|
202
|
+
maturity_dates (pd.Series): Series of maturity dates for the bonds. ytm_rates
|
|
203
|
+
(pd.Series): Series of Yield to Maturity rates corresponding to the
|
|
228
204
|
maturity dates.
|
|
229
205
|
|
|
230
206
|
Returns:
|
|
231
|
-
pd.DataFrame: DataFrame containing the maturity dates and corresponding
|
|
232
|
-
rates.
|
|
207
|
+
pd.DataFrame: DataFrame containing the maturity dates and corresponding real
|
|
208
|
+
spot rates (RSR).
|
|
233
209
|
|
|
234
210
|
Notes:
|
|
235
211
|
The calculation of the spot rates for NTN-B bonds considers the following steps:
|
|
236
212
|
- Map all all possible payment dates up to the longest maturity date.
|
|
237
213
|
- Interpolate the YTM rates in the intermediate payment dates.
|
|
238
214
|
- Calculate the NTN-B quotation for each maturity date.
|
|
239
|
-
- Calculate the spot rates for each maturity date.
|
|
215
|
+
- Calculate the real spot rates (RSR) for each maturity date.
|
|
240
216
|
"""
|
|
241
217
|
# COUPON = (1.06) ** 0.5 - 1 # Coupon without rounding
|
|
242
218
|
# Validate and normalize the settlement date
|
|
243
219
|
settlement_date = dv.normalize_date(settlement_date)
|
|
244
220
|
|
|
245
|
-
#
|
|
246
|
-
|
|
247
|
-
|
|
221
|
+
# Create the interpolator object
|
|
222
|
+
flat_fwd = Interpolator(
|
|
223
|
+
method="flat_forward",
|
|
224
|
+
known_bdays=bday.count(settlement_date, maturity_dates),
|
|
225
|
+
known_rates=ytm_rates,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Generate coupon dates up to the longest maturity date
|
|
229
|
+
all_coupon_dates = coupon_dates_map(
|
|
230
|
+
start=settlement_date,
|
|
231
|
+
end=maturity_dates.max(),
|
|
248
232
|
)
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
df = pd.DataFrame(coupon_dates_all, columns=["MaturityDate"])
|
|
233
|
+
|
|
234
|
+
# Create a DataFrame with all coupon dates
|
|
235
|
+
df = pd.DataFrame(all_coupon_dates, columns=["MaturityDate"])
|
|
253
236
|
|
|
254
237
|
# Add auxiliary columns for calculations
|
|
255
238
|
df["BDays"] = bday.count(settlement_date, df["MaturityDate"])
|
|
256
|
-
df["YTM"] =
|
|
239
|
+
df["YTM"] = df["BDays"].apply(flat_fwd.interpolate)
|
|
257
240
|
df["RSR"] = 0.0
|
|
258
241
|
|
|
259
242
|
# Main loop to calculate spot rates
|
|
@@ -273,15 +256,12 @@ def spot_rates(
|
|
|
273
256
|
# Calculate the present value of the cash flows (discounted cash flows)
|
|
274
257
|
dcfs = cfs / (1 + spot_rates) ** periods
|
|
275
258
|
|
|
276
|
-
#
|
|
259
|
+
# Calculate the real spot rate (RSR) for the bond
|
|
277
260
|
bd = df.at[index, "BDays"]
|
|
278
|
-
ytm =
|
|
261
|
+
ytm = df.at[index, "YTM"]
|
|
279
262
|
ntnb_quotation = quotation(settlement_date, maturity_date, ytm) / 100
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
# Update DataFrame with calculated values
|
|
283
|
-
df.at[index, "RSR"] = spot_rate
|
|
284
|
-
df.at[index, "YTM"] = ytm
|
|
263
|
+
rsr = ((COUPON + 1) / (ntnb_quotation - dcfs.sum())) ** (252 / bd) - 1
|
|
264
|
+
df.at[index, "RSR"] = rsr
|
|
285
265
|
|
|
286
266
|
# Drop the BDays column, remove intermediate cupon dates and reset the index.
|
|
287
267
|
return (
|
|
@@ -383,25 +363,28 @@ def bei_rates(
|
|
|
383
363
|
|
|
384
364
|
# Fetch Nominal Spot Rate (NSR) data
|
|
385
365
|
df_nsr = _get_nsr_df(reference_date)
|
|
386
|
-
known_bdays = df_nsr["BDaysToExp"].to_list()
|
|
387
|
-
known_rates = df_nsr["NSR_DI"].to_list()
|
|
388
366
|
|
|
367
|
+
ffwd = Interpolator(
|
|
368
|
+
method="flat_forward",
|
|
369
|
+
known_bdays=df_nsr["BDaysToExp"],
|
|
370
|
+
known_rates=df_nsr["NSR_DI"],
|
|
371
|
+
)
|
|
389
372
|
# Calculate Real Spot Rate (RSR)
|
|
390
373
|
df = spot_rates(settlement_date, maturity_dates, ytm_rates)
|
|
391
374
|
df = df.rename(columns={"RSR": "RSR"})
|
|
392
375
|
df["BDays"] = bday.count(reference_date, df["MaturityDate"])
|
|
393
|
-
df["NSR_DI"] = df["BDays"].apply(
|
|
394
|
-
lambda x: ip.find_and_interpolate_flat_forward(x, known_bdays, known_rates)
|
|
395
|
-
)
|
|
376
|
+
df["NSR_DI"] = df["BDays"].apply(ffwd.interpolate)
|
|
396
377
|
|
|
397
378
|
# Calculate Breakeven Inflation Rate (BIR)
|
|
398
379
|
df["BIR_DI"] = ((df["NSR_DI"] + 1) / (df["RSR"] + 1)) - 1
|
|
399
380
|
|
|
400
381
|
# Adjust BEI for DI spread in prefixed bonds
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
382
|
+
ffwd = Interpolator(
|
|
383
|
+
method="flat_forward",
|
|
384
|
+
known_bdays=df_nsr["BDaysToExp"],
|
|
385
|
+
known_rates=df_nsr["NSR_PRE"],
|
|
404
386
|
)
|
|
387
|
+
df["NSR_PRE"] = df["BDays"].apply(ffwd.interpolate)
|
|
405
388
|
df["BIR_PRE"] = ((df["NSR_PRE"] + 1) / (df["RSR"] + 1)) - 1
|
|
406
389
|
|
|
407
390
|
cols_reordered = [
|
|
@@ -76,7 +76,7 @@ def di_pre(reference_date: pd.Timestamp) -> pd.DataFrame:
|
|
|
76
76
|
df_di["MaturityDate"] = df_di["MaturityDate"].dt.to_period("M").dt.to_timestamp()
|
|
77
77
|
|
|
78
78
|
# Fetch bond rates, filtering for LTN and NTN-F types
|
|
79
|
-
df_anbima = anbima(
|
|
79
|
+
df_anbima = anbima(["LTN", "NTN-F"], reference_date)
|
|
80
80
|
# Keep only the relevant columns for the output
|
|
81
81
|
keep_columns = ["ReferenceDate", "BondType", "MaturityDate", "IndicativeRate"]
|
|
82
82
|
df_anbima = df_anbima[keep_columns].copy()
|
pyield-0.9.4/pyield/__about__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.9.4"
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import bisect
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def interpolate_flat_forward(
|
|
5
|
-
prev_rate: float, prev_bdays: int, next_rate: float, next_bdays: int, bdays: int
|
|
6
|
-
) -> float:
|
|
7
|
-
"""Performs interest rate interpolation using the flat forward interpolation method
|
|
8
|
-
considering a base of 252 business days.
|
|
9
|
-
|
|
10
|
-
The interpolation is done between two known pairs of vertices (prev_rate,
|
|
11
|
-
prev_business_days) and (next_rate, next_business_days), for a third vertex defined
|
|
12
|
-
by the number of business days 'business_days' where 'prev_business_days <
|
|
13
|
-
business_days < next_business_days'. This third vertex is the point in time for
|
|
14
|
-
which the interest rate is being calculated.
|
|
15
|
-
|
|
16
|
-
Args:
|
|
17
|
-
- prev_rate (float): Interest rate of the previous vertex.
|
|
18
|
-
- prev_bdays (int): Number of business days of the previous vertex.
|
|
19
|
-
- next_rate (float): Interest rate of the next vertex.
|
|
20
|
-
- next_bdays (int): Number of business days of the next vertex.
|
|
21
|
-
- bdays (int): Number of business days for which the interest rate is to be
|
|
22
|
-
interpolated.
|
|
23
|
-
|
|
24
|
-
Example:
|
|
25
|
-
interpolated_rate = interpolate_flat_forward(0.045, 30, 0.05, 60, 45)
|
|
26
|
-
|
|
27
|
-
Returns:
|
|
28
|
-
float: The interpolated interest rate at the given `business_days`.
|
|
29
|
-
"""
|
|
30
|
-
a = (1 + prev_rate) ** (prev_bdays / 252)
|
|
31
|
-
b = (1 + next_rate) ** (next_bdays / 252)
|
|
32
|
-
c = (bdays - prev_bdays) / (next_bdays - prev_bdays)
|
|
33
|
-
|
|
34
|
-
return (a * (b / a) ** c) ** (252 / bdays) - 1
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def find_and_interpolate_flat_forward(
|
|
38
|
-
bdays: int,
|
|
39
|
-
known_bdays: list[int],
|
|
40
|
-
known_rates: list[float],
|
|
41
|
-
) -> float:
|
|
42
|
-
"""
|
|
43
|
-
Finds the appropriate interpolation point and returns the interest rate
|
|
44
|
-
interpolated by the flat forward method from that point. Uses the `bisect` module
|
|
45
|
-
for binary search in ordered lists.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
Args:
|
|
49
|
-
business_days (int): Number of business days for which the flat forward interest
|
|
50
|
-
rate is to be calculated.
|
|
51
|
-
known_business_days (List[int]): List of business days where interest rates are
|
|
52
|
-
known.
|
|
53
|
-
known_rates (List[float]): List of known interest rates.
|
|
54
|
-
|
|
55
|
-
Notes:
|
|
56
|
-
- It is assumed that `known_business_days` and `known_rates` are sorted and have
|
|
57
|
-
the same size.
|
|
58
|
-
- The method uses 252 business days per year in the interpolation, which is the
|
|
59
|
-
standard in the Brazilian market.
|
|
60
|
-
- Special cases are handled for situations where `business_days` is less than
|
|
61
|
-
the first known business day, greater than the last known business day, or
|
|
62
|
-
exactly equal to a known business day, avoiding the need for interpolation.
|
|
63
|
-
|
|
64
|
-
Example:
|
|
65
|
-
known_bdays = [30, 60, 90]
|
|
66
|
-
known_rates = [0.045, 0.05, 0.055]
|
|
67
|
-
interpolated_rate = find_and_interpolate_flat_forward(45, known_bdays, known_rates)
|
|
68
|
-
|
|
69
|
-
Returns:
|
|
70
|
-
float: The interest rate interpolated by the flat forward method for the given
|
|
71
|
-
number of business days.
|
|
72
|
-
"""
|
|
73
|
-
# Special cases
|
|
74
|
-
if bdays <= known_bdays[0]:
|
|
75
|
-
return known_rates[0]
|
|
76
|
-
elif bdays >= known_bdays[-1]:
|
|
77
|
-
return known_rates[-1]
|
|
78
|
-
# Do not interpolate vertex whose rate is known
|
|
79
|
-
elif bdays in known_bdays:
|
|
80
|
-
return known_rates[known_bdays.index(bdays)]
|
|
81
|
-
|
|
82
|
-
# Find i such that known_business_days[i-1] < business_days < known_business_days[i]
|
|
83
|
-
i = bisect.bisect_left(known_bdays, bdays)
|
|
84
|
-
|
|
85
|
-
return interpolate_flat_forward(
|
|
86
|
-
known_rates[i - 1],
|
|
87
|
-
known_bdays[i - 1],
|
|
88
|
-
known_rates[i],
|
|
89
|
-
known_bdays[i],
|
|
90
|
-
bdays,
|
|
91
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|