PYield 0.8.3__tar.gz → 0.8.4__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.8.3 → pyield-0.8.4}/PKG-INFO +1 -1
- pyield-0.8.4/pyield/__about__.py +1 -0
- {pyield-0.8.3 → pyield-0.8.4}/pyield/anbima.py +35 -18
- {pyield-0.8.3 → pyield-0.8.4}/pyield/data_access.py +6 -6
- {pyield-0.8.3 → pyield-0.8.4}/pyield/ntnb.py +42 -32
- {pyield-0.8.3 → pyield-0.8.4}/pyield/spreads.py +1 -1
- {pyield-0.8.3 → pyield-0.8.4}/pyproject.toml +4 -1
- pyield-0.8.3/pyield/__about__.py +0 -1
- {pyield-0.8.3 → pyield-0.8.4}/LICENSE +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/README.md +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/pyield/__init__.py +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/pyield/bday.py +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/pyield/date_validator.py +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/pyield/futures/__init__.py +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/pyield/futures/common.py +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/pyield/futures/historical.py +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/pyield/futures/intraday.py +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/pyield/futures/xml.py +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/pyield/holidays/__init__.py +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/pyield/holidays/br_holidays_new.txt +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/pyield/holidays/br_holidays_old.txt +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/pyield/holidays/core.py +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/pyield/indicators.py +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/pyield/interpolator.py +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/pyield/projections.py +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/pyield/py.typed +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/tests/__init__.py +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/tests/data/SPRD231222.zip +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/tests/data/SPRD240426.zip +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/tests/test_bday.py +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/tests/test_futures.py +0 -0
- {pyield-0.8.3 → pyield-0.8.4}/tests/test_futures_xml.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.8.4"
|
|
@@ -12,6 +12,7 @@ def _get_file_content(reference_date: pd.Timestamp, headers: dict = None) -> str
|
|
|
12
12
|
headers = headers or {} # Default empty headers
|
|
13
13
|
url_date = reference_date.strftime("%y%m%d")
|
|
14
14
|
member_url = f"{ANBIMA_MEMBER_URL}ms{url_date}.txt"
|
|
15
|
+
# Non member url example: https://www.anbima.com.br/informacoes/merc-sec/arqs/ms240614.txt
|
|
15
16
|
non_member_url = f"{ANBIMA_NON_MEMBER_URL}ms{url_date}.txt"
|
|
16
17
|
|
|
17
18
|
# Tries to access the member URL first
|
|
@@ -49,40 +50,37 @@ def _read_raw_df(file_content: str) -> pd.DataFrame:
|
|
|
49
50
|
|
|
50
51
|
def _process_raw_df(df_raw: pd.DataFrame) -> pd.DataFrame:
|
|
51
52
|
"""
|
|
52
|
-
Process raw data from ANBIMA by
|
|
53
|
-
adjusting data formats.
|
|
53
|
+
Process raw data from ANBIMA by renaming columns and adjusting data formats.
|
|
54
54
|
|
|
55
55
|
Parameters:
|
|
56
|
-
|
|
56
|
+
df (pd.DataFrame): Raw DataFrame to process.
|
|
57
57
|
|
|
58
58
|
Returns:
|
|
59
|
-
|
|
59
|
+
pd.DataFrame: Processed DataFrame.
|
|
60
60
|
"""
|
|
61
61
|
# Filter selected columns and rename them
|
|
62
62
|
selected_columns_dict = {
|
|
63
63
|
"Titulo": "BondType",
|
|
64
64
|
"Data Referencia": "ReferenceDate",
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
"Codigo SELIC": "SelicCode",
|
|
66
|
+
"Data Base/Emissao": "IssueBaseDate",
|
|
67
67
|
"Data Vencimento": "MaturityDate",
|
|
68
68
|
"Tx. Compra": "BidRate",
|
|
69
69
|
"Tx. Venda": "AskRate",
|
|
70
70
|
"Tx. Indicativas": "IndicativeRate",
|
|
71
71
|
"PU": "Price",
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
"Desvio padrao": "StdDev",
|
|
73
|
+
"Interv. Ind. Inf. (D0)": "LowerBoundRateD0",
|
|
74
|
+
"Interv. Ind. Sup. (D0)": "UpperBoundRateD0",
|
|
75
|
+
"Interv. Ind. Inf. (D+1)": "LowerBoundRateD1",
|
|
76
|
+
"Interv. Ind. Sup. (D+1)": "UpperBoundRateD1",
|
|
77
|
+
"Criterio": "Criteria",
|
|
78
78
|
}
|
|
79
|
-
|
|
80
|
-
df = df_raw[select_columns].copy()
|
|
81
|
-
df = df.rename(columns=selected_columns_dict)
|
|
79
|
+
df = df_raw.rename(columns=selected_columns_dict)
|
|
82
80
|
|
|
83
81
|
# Remove percentage from rates
|
|
84
|
-
rate_cols = [
|
|
85
|
-
df[rate_cols] = df[rate_cols] / 100
|
|
82
|
+
rate_cols = [col for col in df.columns if "Rate" in col]
|
|
83
|
+
df[rate_cols] = (df[rate_cols] / 100).round(6)
|
|
86
84
|
|
|
87
85
|
df["ReferenceDate"] = pd.to_datetime(df["ReferenceDate"], format="%Y%m%d")
|
|
88
86
|
df["MaturityDate"] = pd.to_datetime(df["MaturityDate"], format="%Y%m%d")
|
|
@@ -90,7 +88,7 @@ def _process_raw_df(df_raw: pd.DataFrame) -> pd.DataFrame:
|
|
|
90
88
|
return df.sort_values(["BondType", "MaturityDate"], ignore_index=True)
|
|
91
89
|
|
|
92
90
|
|
|
93
|
-
def
|
|
91
|
+
def fetch_data(reference_date: pd.Timestamp, headers: dict = None) -> pd.DataFrame:
|
|
94
92
|
"""
|
|
95
93
|
Fetches indicative treasury rates from ANBIMA for a specified reference date.
|
|
96
94
|
|
|
@@ -114,3 +112,22 @@ def fetch_bonds(reference_date: pd.Timestamp, headers: dict = None) -> pd.DataFr
|
|
|
114
112
|
df = _read_raw_df(file_content)
|
|
115
113
|
|
|
116
114
|
return _process_raw_df(df)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def fetch_rates(reference_date: pd.Timestamp, headers: dict = None) -> pd.DataFrame:
|
|
118
|
+
"""
|
|
119
|
+
Fetches indicative treasury rates from ANBIMA for a specified reference date.
|
|
120
|
+
|
|
121
|
+
This function retrieves the indicative rates for Brazilian treasury securities
|
|
122
|
+
from ANBIMA, processing them into a structured pandas DataFrame.
|
|
123
|
+
|
|
124
|
+
Parameters:
|
|
125
|
+
reference_date (pd.Timestamp): The date for which to fetch the indicative rates.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
pd.DataFrame: A DataFrame containing the processed indicative rates for the
|
|
129
|
+
given reference date.
|
|
130
|
+
|
|
131
|
+
"""
|
|
132
|
+
df = fetch_data(reference_date, headers)
|
|
133
|
+
return df[["BondType", "MaturityDate", "IndicativeRate"]].copy()
|
|
@@ -17,7 +17,7 @@ def fetch_asset(
|
|
|
17
17
|
Args:
|
|
18
18
|
asset_code (str): The asset code identifying the type of financial asset.
|
|
19
19
|
Supported options:
|
|
20
|
-
- "TRB":
|
|
20
|
+
- "TRB": Brazilian treasury bonds (indicative rates from ANBIMA).
|
|
21
21
|
- "LTN", "LFT", "NTN-F", "NTN-B": Specific types of Brazilian treasury bonds
|
|
22
22
|
(indicative rates from ANBIMA).
|
|
23
23
|
- "DI1": One-day Interbank Deposit Futures (Futuro de DI) from B3.
|
|
@@ -42,7 +42,7 @@ def fetch_asset(
|
|
|
42
42
|
ValueError: If the asset code is not recognized or supported.
|
|
43
43
|
|
|
44
44
|
Examples:
|
|
45
|
-
>>> fetch_asset("
|
|
45
|
+
>>> fetch_asset("LTN", "31-05-2024")
|
|
46
46
|
>>> fetch_asset("DI1", "31-05-2024")
|
|
47
47
|
"""
|
|
48
48
|
SUPPORTED_BONDS = ["LTN", "LFT", "NTN-F", "NTN-B"]
|
|
@@ -55,13 +55,13 @@ def fetch_asset(
|
|
|
55
55
|
return ft.fetch_intraday_df(future_code=asset_code.upper())
|
|
56
56
|
|
|
57
57
|
if asset_code.upper() == "TRB":
|
|
58
|
-
return an.
|
|
58
|
+
return an.fetch_data(reference_date=normalized_date)
|
|
59
59
|
|
|
60
60
|
if asset_code.upper() in SUPPORTED_BONDS:
|
|
61
|
-
df_bond = an.
|
|
61
|
+
df_bond = an.fetch_data(reference_date=normalized_date)
|
|
62
62
|
df_bond.query(f"BondType == '{asset_code.upper()}'", inplace=True)
|
|
63
|
-
# Return the DataFrame
|
|
64
|
-
return df_bond.
|
|
63
|
+
# Return the DataFrame with the index reset
|
|
64
|
+
return df_bond.reset_index(drop=True)
|
|
65
65
|
|
|
66
66
|
if asset_code.upper() in SUPPORTED_FUTURES:
|
|
67
67
|
return ft.fetch_historical_df(
|
|
@@ -23,7 +23,7 @@ def anbima_data(reference_date: str | pd.Timestamp) -> pd.DataFrame:
|
|
|
23
23
|
return da.fetch_asset(asset_code="NTN-B", reference_date=reference_date)
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
def
|
|
26
|
+
def ytm_rates(reference_date: str | pd.Timestamp) -> pd.DataFrame:
|
|
27
27
|
"""
|
|
28
28
|
Fetch NTN-B Anbima indicative rates for the given reference date.
|
|
29
29
|
|
|
@@ -33,10 +33,12 @@ def anbima_rates(reference_date: str | pd.Timestamp) -> pd.DataFrame:
|
|
|
33
33
|
Returns:
|
|
34
34
|
pd.DataFrame: A DataFrame containing the maturity dates and corresponding rates.
|
|
35
35
|
"""
|
|
36
|
-
|
|
36
|
+
df = anbima_data(reference_date)[["MaturityDate", "IndicativeRate"]]
|
|
37
|
+
# Rename IndicativeRate to YTM for consistency
|
|
38
|
+
return df.rename(columns={"IndicativeRate": "YTM"})
|
|
37
39
|
|
|
38
40
|
|
|
39
|
-
def
|
|
41
|
+
def _truncate(value, decimal_places):
|
|
40
42
|
"""
|
|
41
43
|
Truncate a float or a Pandas Series to the specified decimal place.
|
|
42
44
|
|
|
@@ -178,16 +180,16 @@ def quotation(
|
|
|
178
180
|
cf = np.where(payment_dates == maturity_date, FINAL_PMT, INTER_PMT)
|
|
179
181
|
|
|
180
182
|
# Calculate the number of periods truncated to 14 decimal places
|
|
181
|
-
n =
|
|
183
|
+
n = _truncate(bdays / 252, 14)
|
|
182
184
|
|
|
183
185
|
# Calculate the present value of each cash flow (DCF) rounded to 10 decimal places
|
|
184
186
|
dcf = (cf / (1 + discount_rate) ** n).round(10)
|
|
185
187
|
|
|
186
188
|
# Return the quotation (the dcf sum) truncated to 4 decimal places
|
|
187
|
-
return
|
|
189
|
+
return _truncate(dcf.sum(), 4)
|
|
188
190
|
|
|
189
191
|
|
|
190
|
-
def
|
|
192
|
+
def _prepare_interpolation_data(
|
|
191
193
|
reference_date: pd.Timestamp, maturity_dates: pd.Series, rates: pd.Series
|
|
192
194
|
) -> tuple:
|
|
193
195
|
"""
|
|
@@ -242,7 +244,7 @@ def spot_rates(
|
|
|
242
244
|
settlement_date = dv.normalize_date(settlement_date)
|
|
243
245
|
|
|
244
246
|
# Prepare the data for interpolation
|
|
245
|
-
ordered_bdays, ordered_ytms =
|
|
247
|
+
ordered_bdays, ordered_ytms = _prepare_interpolation_data(
|
|
246
248
|
settlement_date, maturity_dates, ytm_rates
|
|
247
249
|
)
|
|
248
250
|
# Generate coupon dates and initialize the main DataFrame
|
|
@@ -253,7 +255,7 @@ def spot_rates(
|
|
|
253
255
|
# Add auxiliary columns for calculations
|
|
254
256
|
df["BDays"] = bday.count_bdays(settlement_date, df["MaturityDate"])
|
|
255
257
|
df["YTM"] = 0.0
|
|
256
|
-
df["
|
|
258
|
+
df["RSR"] = 0.0
|
|
257
259
|
|
|
258
260
|
# Main loop to calculate spot rates
|
|
259
261
|
for index in df.index:
|
|
@@ -266,7 +268,7 @@ def spot_rates(
|
|
|
266
268
|
|
|
267
269
|
# Create the Series that will be used to calculate the discounted cash flows
|
|
268
270
|
cfs = pd.Series(COUPON, index=dfl.index)
|
|
269
|
-
spot_rates = dfl["
|
|
271
|
+
spot_rates = dfl["RSR"]
|
|
270
272
|
periods = dfl["BDays"] / 252
|
|
271
273
|
|
|
272
274
|
# Calculate the present value of the cash flows (discounted cash flows)
|
|
@@ -279,7 +281,7 @@ def spot_rates(
|
|
|
279
281
|
spot_rate = ((COUPON + 1) / (ntnb_quotation - dcfs.sum())) ** (252 / bd) - 1
|
|
280
282
|
|
|
281
283
|
# Update DataFrame with calculated values
|
|
282
|
-
df.at[index, "
|
|
284
|
+
df.at[index, "RSR"] = spot_rate
|
|
283
285
|
df.at[index, "YTM"] = ytm
|
|
284
286
|
|
|
285
287
|
# Drop the BDays column, remove intermediate cupon dates and reset the index.
|
|
@@ -290,7 +292,7 @@ def spot_rates(
|
|
|
290
292
|
)
|
|
291
293
|
|
|
292
294
|
|
|
293
|
-
def
|
|
295
|
+
def _get_nsr_df(reference_date: pd.Timestamp) -> pd.DataFrame:
|
|
294
296
|
"""
|
|
295
297
|
Fetch the Nominal Interest Rate (NIR) data for NTN-B bonds.
|
|
296
298
|
|
|
@@ -345,14 +347,14 @@ def _get_nir_df(reference_date: pd.Timestamp) -> pd.DataFrame:
|
|
|
345
347
|
return df
|
|
346
348
|
|
|
347
349
|
|
|
348
|
-
def
|
|
350
|
+
def bei_rates(
|
|
349
351
|
reference_date: str | pd.Timestamp,
|
|
350
352
|
settlement_date: str | pd.Timestamp,
|
|
351
353
|
maturity_dates: pd.Series,
|
|
352
354
|
ytm_rates: pd.Series,
|
|
353
355
|
) -> pd.DataFrame:
|
|
354
356
|
"""
|
|
355
|
-
Calculate the Breakeven Inflation (
|
|
357
|
+
Calculate the Breakeven Inflation (BEI) for NTN-B bonds based on nominal and real
|
|
356
358
|
interest rates.
|
|
357
359
|
|
|
358
360
|
Args:
|
|
@@ -364,45 +366,53 @@ def breakeven_inflation(
|
|
|
364
366
|
to the maturity dates.
|
|
365
367
|
|
|
366
368
|
Returns:
|
|
367
|
-
pd.DataFrame: DataFrame containing the
|
|
368
|
-
|
|
369
|
-
|
|
369
|
+
pd.DataFrame: DataFrame containing the breakeven inflation rates.
|
|
370
|
+
|
|
371
|
+
Returned columns:
|
|
372
|
+
- MaturityDate: Maturity date of the bond.
|
|
373
|
+
- BDays: Number of business days from the settlement date to the maturity.
|
|
374
|
+
- YTM: Yield to Maturity rate for the bond.
|
|
375
|
+
- RSR: Real Spot Rate for the bond.
|
|
376
|
+
- NSR_DI: Nominal Spot Rate for the bond.
|
|
377
|
+
- NSR_PRE: Nominal Spot Rate for the bond with DI spread.
|
|
378
|
+
- BIR_DI: Breakeven Inflation Rate for the bond.
|
|
379
|
+
- BIR_PRE: Breakeven Inflation Rate for the bond adjusted for DI spread.
|
|
370
380
|
"""
|
|
371
381
|
# Normalize input dates
|
|
372
382
|
reference_date = dv.normalize_date(reference_date)
|
|
373
383
|
settlement_date = dv.normalize_date(settlement_date)
|
|
374
384
|
|
|
375
385
|
# Fetch Nominal Spot Rate (NSR) data
|
|
376
|
-
|
|
377
|
-
known_bdays =
|
|
378
|
-
known_rates =
|
|
386
|
+
df_nsr = _get_nsr_df(reference_date)
|
|
387
|
+
known_bdays = df_nsr["BDaysToExp"].to_list()
|
|
388
|
+
known_rates = df_nsr["NSR_DI"].to_list()
|
|
379
389
|
|
|
380
390
|
# Calculate Real Spot Rate (RSR)
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
391
|
+
df = spot_rates(settlement_date, maturity_dates, ytm_rates)
|
|
392
|
+
df = df.rename(columns={"RSR": "RSR"})
|
|
393
|
+
df["BDays"] = bday.count_bdays(reference_date, df["MaturityDate"])
|
|
394
|
+
df["NSR_DI"] = df["BDays"].apply(
|
|
385
395
|
lambda x: ip.find_and_interpolate_flat_forward(x, known_bdays, known_rates)
|
|
386
396
|
)
|
|
387
397
|
|
|
388
|
-
# Calculate Breakeven Inflation (
|
|
389
|
-
|
|
398
|
+
# Calculate Breakeven Inflation Rate (BIR)
|
|
399
|
+
df["BIR_DI"] = ((df["NSR_DI"] + 1) / (df["RSR"] + 1)) - 1
|
|
390
400
|
|
|
391
401
|
# Adjust BEI for DI spread in prefixed bonds
|
|
392
|
-
known_rates =
|
|
393
|
-
|
|
402
|
+
known_rates = df_nsr["NSR_PRE"].to_list()
|
|
403
|
+
df["NSR_PRE"] = df["BDays"].apply(
|
|
394
404
|
lambda x: ip.find_and_interpolate_flat_forward(x, known_bdays, known_rates)
|
|
395
405
|
)
|
|
396
|
-
|
|
406
|
+
df["BIR_PRE"] = ((df["NSR_PRE"] + 1) / (df["RSR"] + 1)) - 1
|
|
397
407
|
|
|
398
|
-
|
|
408
|
+
cols_reordered = [
|
|
399
409
|
"MaturityDate",
|
|
400
410
|
"BDays",
|
|
401
411
|
"YTM",
|
|
402
412
|
"RSR",
|
|
403
413
|
"NSR_DI",
|
|
404
414
|
"NSR_PRE",
|
|
405
|
-
"
|
|
406
|
-
"
|
|
415
|
+
"BIR_DI",
|
|
416
|
+
"BIR_PRE",
|
|
407
417
|
]
|
|
408
|
-
return
|
|
418
|
+
return df[cols_reordered].copy()
|
|
@@ -38,7 +38,7 @@ def calculate_di_spreads(reference_date: pd.Timestamp) -> pd.DataFrame:
|
|
|
38
38
|
df_di["MaturityDate"] = df_di["MaturityDate"].dt.to_period("M").dt.to_timestamp()
|
|
39
39
|
|
|
40
40
|
# Fetch bond rates, filtering for LTN and NTN-F types
|
|
41
|
-
df_anbima = an.
|
|
41
|
+
df_anbima = an.fetch_data(reference_date, False)
|
|
42
42
|
df_anbima.query("BondType in ['LTN', 'NTN-F']", inplace=True)
|
|
43
43
|
|
|
44
44
|
# Merge bond and DI rates by maturity date to calculate spreads
|
|
@@ -32,7 +32,7 @@ dependencies = [
|
|
|
32
32
|
"requests>=2.31.0",
|
|
33
33
|
]
|
|
34
34
|
dynamic = []
|
|
35
|
-
version = "0.8.
|
|
35
|
+
version = "0.8.4"
|
|
36
36
|
|
|
37
37
|
[project.license]
|
|
38
38
|
file = "LICENSE"
|
|
@@ -52,6 +52,9 @@ dev = [
|
|
|
52
52
|
"ipykernel",
|
|
53
53
|
"pytest",
|
|
54
54
|
"mypy",
|
|
55
|
+
"mkdocs",
|
|
56
|
+
"mkdocs-material",
|
|
57
|
+
"mkdocstrings[python]",
|
|
55
58
|
]
|
|
56
59
|
|
|
57
60
|
[tool.ruff]
|
pyield-0.8.3/pyield/__about__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.8.3"
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|