PYield 0.12.2__tar.gz → 0.13.0__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.
Files changed (37) hide show
  1. {pyield-0.12.2 → pyield-0.13.0}/PKG-INFO +1 -1
  2. pyield-0.13.0/pyield/__about__.py +1 -0
  3. {pyield-0.12.2 → pyield-0.13.0}/pyield/bonds/lft.py +15 -15
  4. {pyield-0.12.2 → pyield-0.13.0}/pyield/bonds/ltn.py +12 -12
  5. {pyield-0.12.2 → pyield-0.13.0}/pyield/bonds/ntnb.py +62 -77
  6. {pyield-0.12.2 → pyield-0.13.0}/pyield/bonds/ntnf.py +101 -85
  7. {pyield-0.12.2 → pyield-0.13.0}/pyield/bonds/utils.py +3 -9
  8. {pyield-0.12.2 → pyield-0.13.0}/pyield/di.py +38 -31
  9. {pyield-0.12.2 → pyield-0.13.0}/pyield/fetchers/anbima.py +2 -3
  10. {pyield-0.12.2 → pyield-0.13.0}/pyproject.toml +1 -1
  11. pyield-0.12.2/pyield/__about__.py +0 -1
  12. {pyield-0.12.2 → pyield-0.13.0}/LICENSE +0 -0
  13. {pyield-0.12.2 → pyield-0.13.0}/README.md +0 -0
  14. {pyield-0.12.2 → pyield-0.13.0}/pyield/__init__.py +0 -0
  15. {pyield-0.12.2 → pyield-0.13.0}/pyield/bday.py +0 -0
  16. {pyield-0.12.2 → pyield-0.13.0}/pyield/bonds/__init__.py +0 -0
  17. {pyield-0.12.2 → pyield-0.13.0}/pyield/date_converter.py +0 -0
  18. {pyield-0.12.2 → pyield-0.13.0}/pyield/fetchers/__init__.py +0 -0
  19. {pyield-0.12.2 → pyield-0.13.0}/pyield/fetchers/futures_data/__init__.py +0 -0
  20. {pyield-0.12.2 → pyield-0.13.0}/pyield/fetchers/futures_data/common.py +0 -0
  21. {pyield-0.12.2 → pyield-0.13.0}/pyield/fetchers/futures_data/historical.py +0 -0
  22. {pyield-0.12.2 → pyield-0.13.0}/pyield/fetchers/futures_data/intraday.py +0 -0
  23. {pyield-0.12.2 → pyield-0.13.0}/pyield/fetchers/futures_data/xml.py +0 -0
  24. {pyield-0.12.2 → pyield-0.13.0}/pyield/fetchers/indicators.py +0 -0
  25. {pyield-0.12.2 → pyield-0.13.0}/pyield/fetchers/projections.py +0 -0
  26. {pyield-0.12.2 → pyield-0.13.0}/pyield/holidays/__init__.py +0 -0
  27. {pyield-0.12.2 → pyield-0.13.0}/pyield/holidays/br_holidays_new.txt +0 -0
  28. {pyield-0.12.2 → pyield-0.13.0}/pyield/holidays/br_holidays_old.txt +0 -0
  29. {pyield-0.12.2 → pyield-0.13.0}/pyield/holidays/holidays_manager.py +0 -0
  30. {pyield-0.12.2 → pyield-0.13.0}/pyield/interpolator.py +0 -0
  31. {pyield-0.12.2 → pyield-0.13.0}/pyield/py.typed +0 -0
  32. {pyield-0.12.2 → pyield-0.13.0}/tests/__init__.py +0 -0
  33. {pyield-0.12.2 → pyield-0.13.0}/tests/data/SPRD231222.zip +0 -0
  34. {pyield-0.12.2 → pyield-0.13.0}/tests/data/SPRD240426.zip +0 -0
  35. {pyield-0.12.2 → pyield-0.13.0}/tests/test_bday.py +0 -0
  36. {pyield-0.12.2 → pyield-0.13.0}/tests/test_futures.py +0 -0
  37. {pyield-0.12.2 → pyield-0.13.0}/tests/test_futures_xml.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PYield
3
- Version: 0.12.2
3
+ Version: 0.13.0
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>
@@ -0,0 +1 @@
1
+ __version__ = "0.13.0"
@@ -32,7 +32,7 @@ def anbima_rates(reference_date: str | pd.Timestamp) -> pd.Series:
32
32
  return an.get_anbima_rates(reference_date, "LFT")
33
33
 
34
34
 
35
- def anbima_historical_rates(maturity_date: str | pd.Timestamp) -> pd.Series:
35
+ def anbima_historical_rates(maturity: str | pd.Timestamp) -> pd.Series:
36
36
  """
37
37
  Fetch historical LFT Anbima indicative rates for the given maturity date.
38
38
 
@@ -42,21 +42,21 @@ def anbima_historical_rates(maturity_date: str | pd.Timestamp) -> pd.Series:
42
42
  Returns:
43
43
  pd.Series: A Series containing the rates indexed by reference date.
44
44
  """
45
- return an.get_anbima_historical_rates("LFT", maturity_date)
45
+ return an.get_anbima_historical_rates("LFT", maturity)
46
46
 
47
47
 
48
48
  def quotation(
49
- settlement_date: str | pd.Timestamp,
50
- maturity_date: str | pd.Timestamp,
51
- yield_rate: float,
49
+ settlement: str | pd.Timestamp,
50
+ maturity: str | pd.Timestamp,
51
+ rate: float,
52
52
  ) -> float:
53
53
  """
54
54
  Calculate the quotation of a LFT bond using Anbima rules.
55
55
 
56
56
  Args:
57
- settlement_date (str | pd.Timestamp): The settlement date of the bond.
58
- maturity_date (str | pd.Timestamp): The maturity date of the bond.
59
- yield_rate (float): The annualized yield of the bond
57
+ settlement (str | pd.Timestamp): The settlement date of the bond.
58
+ maturity (str | pd.Timestamp): The maturity date of the bond.
59
+ rate (float): The annualized yield rate of the bond
60
60
 
61
61
  Returns:
62
62
  float: The quotation of the bond.
@@ -64,22 +64,22 @@ def quotation(
64
64
  Examples:
65
65
  Calculate the quotation of a LFT bond with a 0.02 yield rate:
66
66
  >>> lft.quotation(
67
- ... settlement_date="24-07-2024",
68
- ... maturity_date="01-09-2030",
69
- ... yield_rate=0.001717, # 0.1717%
67
+ ... settlement="24-07-2024",
68
+ ... maturity="01-09-2030",
69
+ ... rate=0.001717, # 0.1717%
70
70
  ... )
71
71
  98.9645
72
72
  """
73
73
  # Validate and normalize dates
74
- settlement_date = dc.convert_date(settlement_date)
75
- maturity_date = dc.convert_date(maturity_date)
74
+ settlement = dc.convert_date(settlement)
75
+ maturity = dc.convert_date(maturity)
76
76
 
77
77
  # The number of bdays between settlement (inclusive) and the maturity (exclusive)
78
- bdays = bday.count(settlement_date, maturity_date)
78
+ bdays = bday.count(settlement, maturity)
79
79
 
80
80
  # Calculate the number of periods truncated as per Anbima rules
81
81
  num_of_years = ut.truncate(bdays / 252, 14)
82
82
 
83
- discount_factor = 1 / (1 + yield_rate) ** num_of_years
83
+ discount_factor = 1 / (1 + rate) ** num_of_years
84
84
 
85
85
  return ut.truncate(100 * discount_factor, 4)
@@ -34,7 +34,7 @@ def anbima_rates(reference_date: str | pd.Timestamp) -> pd.Series:
34
34
  return an.get_anbima_rates(reference_date, "LTN")
35
35
 
36
36
 
37
- def anbima_historical_rates(maturity_date: str | pd.Timestamp) -> pd.Series:
37
+ def anbima_historical_rates(maturity: str | pd.Timestamp) -> pd.Series:
38
38
  """
39
39
  Fetch historical LTN Anbima indicative rates for the given maturity date.
40
40
 
@@ -44,23 +44,23 @@ def anbima_historical_rates(maturity_date: str | pd.Timestamp) -> pd.Series:
44
44
  Returns:
45
45
  pd.Series: A Series containing the rates for the given maturity date.
46
46
  """
47
- return an.get_anbima_historical_rates("LTN", maturity_date)
47
+ return an.get_anbima_historical_rates("LTN", maturity)
48
48
 
49
49
 
50
50
  def price(
51
- settlement_date: str | pd.Timestamp,
52
- maturity_date: str | pd.Timestamp,
53
- discount_rate: float,
51
+ settlement: str | pd.Timestamp,
52
+ maturity: str | pd.Timestamp,
53
+ rate: float,
54
54
  ) -> float:
55
55
  """
56
56
  Calculate the LTN price using Anbima rules.
57
57
 
58
58
  Args:
59
- settlement_date (str | pd.Timestamp): The settlement date in 'DD-MM-YYYY' format
59
+ settlement (str | pd.Timestamp): The settlement date in 'DD-MM-YYYY' format
60
60
  or a pandas Timestamp.
61
- maturity_date (str | pd.Timestamp): The maturity date in 'DD-MM-YYYY' format or
61
+ maturity (str | pd.Timestamp): The maturity date in 'DD-MM-YYYY' format or
62
62
  a pandas Timestamp.
63
- discount_rate (float): The discount rate used to calculate the present value of
63
+ rate (float): The discount rate used to calculate the present value of
64
64
  the cash flows, which is the yield to maturity (YTM) of the NTN-F.
65
65
 
66
66
  Returns:
@@ -75,16 +75,16 @@ def price(
75
75
  """
76
76
 
77
77
  # Validate and normalize dates
78
- settlement_date = dc.convert_date(settlement_date)
79
- maturity_date = dc.convert_date(maturity_date)
78
+ settlement = dc.convert_date(settlement)
79
+ maturity = dc.convert_date(maturity)
80
80
 
81
81
  # Calculate the number of business days between settlement and cash flow dates
82
- bdays = bday.count(settlement_date, maturity_date)
82
+ bdays = bday.count(settlement, maturity)
83
83
 
84
84
  # Calculate the number of periods truncated as per Anbima rule
85
85
  num_of_years = ut.truncate(bdays / 252, 14)
86
86
 
87
- discount_factor = (1 + discount_rate) ** num_of_years
87
+ discount_factor = (1 + rate) ** num_of_years
88
88
 
89
89
  # Truncate the price to 6 decimal places as per Anbima rules
90
90
  return ut.truncate(FACE_VALUE / discount_factor, 6)
@@ -47,7 +47,7 @@ def anbima_rates(reference_date: str | pd.Timestamp) -> pd.Series:
47
47
  return an.get_anbima_rates(reference_date, "NTN-B")
48
48
 
49
49
 
50
- def anbima_historical_rates(maturity_date: str | pd.Timestamp) -> pd.Series:
50
+ def anbima_historical_rates(maturity: str | pd.Timestamp) -> pd.Series:
51
51
  """
52
52
  Fetch historical NTN-B Anbima indicative rates for the given maturity date.
53
53
 
@@ -57,7 +57,7 @@ def anbima_historical_rates(maturity_date: str | pd.Timestamp) -> pd.Series:
57
57
  Returns:
58
58
  pd.Series: A Series containing the rates for the given maturity date.
59
59
  """
60
- return an.get_anbima_historical_rates("NTN-B", maturity_date)
60
+ return an.get_anbima_historical_rates("NTN-B", maturity)
61
61
 
62
62
 
63
63
  def _coupon_dates_map(
@@ -96,8 +96,8 @@ def _coupon_dates_map(
96
96
 
97
97
 
98
98
  def coupon_dates(
99
- start_date: str | pd.Timestamp,
100
- maturity_date: str | pd.Timestamp,
99
+ start: str | pd.Timestamp,
100
+ maturity: str | pd.Timestamp,
101
101
  ) -> pd.Series:
102
102
  """
103
103
  Generate all remaining coupon dates between a given date and the maturity date.
@@ -113,23 +113,23 @@ def coupon_dates(
113
113
  list[pd.Timestamp]: List of coupon dates between start and maturity dates.
114
114
  """
115
115
  # Validate and normalize dates
116
- start_date = dc.convert_date(start_date)
117
- maturity_date = dc.convert_date(maturity_date)
116
+ start = dc.convert_date(start)
117
+ maturity = dc.convert_date(maturity)
118
118
 
119
119
  # Check if maturity date is after the start date
120
- if maturity_date < start_date:
120
+ if maturity < start:
121
121
  raise ValueError("Maturity date must be after the start date.")
122
122
 
123
123
  # Check if the maturity date is a valid NTN-B maturity date
124
- if maturity_date.day != COUPON_DAY or maturity_date.month not in COUPON_MONTHS:
124
+ if maturity.day != COUPON_DAY or maturity.month not in COUPON_MONTHS:
125
125
  raise ValueError("NTN-B maturity must be 15/02, 15/05, 15/08, or 15/11.")
126
126
 
127
127
  # Initialize loop variables
128
- cp_date = maturity_date
128
+ cp_date = maturity
129
129
  cp_dates = []
130
130
 
131
131
  # Iterate backwards from the maturity date to the settlement date
132
- while cp_date >= start_date:
132
+ while cp_date >= start:
133
133
  cp_dates.append(cp_date)
134
134
  # Move the coupon date back 6 months
135
135
  cp_date -= pd.DateOffset(months=6)
@@ -138,19 +138,19 @@ def coupon_dates(
138
138
 
139
139
 
140
140
  def quotation(
141
- settlement_date: str | pd.Timestamp,
142
- maturity_date: str | pd.Timestamp,
143
- discount_rate: float,
141
+ settlement: str | pd.Timestamp,
142
+ maturity: str | pd.Timestamp,
143
+ rate: float,
144
144
  ) -> float:
145
145
  """
146
146
  Calculate the NTN-B quotation in base 100 using Anbima rules.
147
147
 
148
148
  Args:
149
- settlement_date (str | pd.Timestamp): The settlement date in 'DD-MM-YYYY' format
149
+ settlement (str | pd.Timestamp): The settlement date in 'DD-MM-YYYY' format
150
150
  or a pandas Timestamp.
151
- maturity_date (str | pd.Timestamp): The maturity date in 'DD-MM-YYYY' format or
151
+ maturity (str | pd.Timestamp): The maturity date in 'DD-MM-YYYY' format or
152
152
  a pandas Timestamp.
153
- discount_rate (float): The discount rate used to calculate the present value of
153
+ rate (float): The discount rate used to calculate the present value of
154
154
  the cash flows, which is the yield to maturity (YTM) of the NTN-B.
155
155
 
156
156
  Returns:
@@ -169,25 +169,25 @@ def quotation(
169
169
  99.5341
170
170
  """
171
171
  # Validate and normalize dates
172
- settlement_date = dc.convert_date(settlement_date)
173
- maturity_date = dc.convert_date(maturity_date)
172
+ settlement = dc.convert_date(settlement)
173
+ maturity = dc.convert_date(maturity)
174
174
 
175
175
  # Get the coupon dates between the settlement and maturity dates
176
- payment_dates = coupon_dates(settlement_date, maturity_date)
176
+ payment_dates = coupon_dates(settlement, maturity)
177
177
 
178
178
  # Coupon payment dates must be after the settlement date
179
- payment_dates = payment_dates[payment_dates > settlement_date]
179
+ payment_dates = payment_dates[payment_dates > settlement]
180
180
 
181
181
  # Calculate the number of business days between settlement and cash flow dates
182
- bdays = bday.count(settlement_date, payment_dates)
182
+ bdays = bday.count(settlement, payment_dates)
183
183
 
184
184
  # Set the cash flow at maturity to FINAL_PMT and the others to COUPON_PMT
185
- cash_flows = np.where(payment_dates == maturity_date, FINAL_PMT, COUPON_PMT)
185
+ cash_flows = np.where(payment_dates == maturity, FINAL_PMT, COUPON_PMT)
186
186
 
187
187
  # Calculate the number of periods truncated as per Anbima rules
188
188
  num_of_years = ut.truncate(bdays / 252, 14)
189
189
 
190
- discount_factor = (1 + discount_rate) ** num_of_years
190
+ discount_factor = (1 + rate) ** num_of_years
191
191
 
192
192
  # Calculate the present value of each cash flow (DCF) rounded as per Anbima rules
193
193
  discounted_cash_flows = (cash_flows / discount_factor).round(10)
@@ -222,26 +222,30 @@ def price(
222
222
 
223
223
  def _calculate_coupons_pv(
224
224
  bootstrap_df: pd.DataFrame,
225
- settlement_date: pd.Timestamp,
226
- maturity_date: pd.Timestamp,
225
+ settlement: pd.Timestamp,
226
+ maturity: pd.Timestamp,
227
227
  ) -> float:
228
228
  # Create a subset DataFrame with only the coupon payments (without last payment)
229
- cp_dates_wo_last = coupon_dates(settlement_date, maturity_date)[:-1] # noqa
229
+ cp_dates_wo_last = coupon_dates(settlement, maturity)[:-1] # noqa
230
+ if len(cp_dates_wo_last) == 0:
231
+ return 0
232
+
230
233
  df_coupons = bootstrap_df.query("MaturityDate in @cp_dates_wo_last").copy()
231
234
  df_coupons["Coupon"] = COUPON_PMT
232
235
 
233
236
  # Calculate the present value of the coupon payments
234
237
  pv = ut.calculate_present_value(
235
238
  cash_flows=df_coupons["Coupon"],
236
- discount_rates=df_coupons["SpotRate"],
237
- time_periods=df_coupons["BDays"] / 252,
239
+ rates=df_coupons["SpotRate"],
240
+ periods=df_coupons["BDays"] / 252,
238
241
  )
239
242
  return pv
240
243
 
241
244
 
242
245
  def spot_rates(
243
- settlement_date: str | pd.Timestamp,
244
- ytm_rates: pd.Series,
246
+ settlement: str | pd.Timestamp,
247
+ rates: list[float] | pd.Series,
248
+ maturities: list[str | pd.Timestamp],
245
249
  ) -> pd.DataFrame:
246
250
  """
247
251
  Calculate the spot rates for NTN-B bonds using the bootstrap method.
@@ -269,24 +273,24 @@ def spot_rates(
269
273
  - Calculate the real spot rates for each maturity date.
270
274
  """
271
275
  # Process and validate the input data
272
- settlement_date = dc.convert_date(settlement_date)
273
- ytm_rates = ut.standardize_rates(ytm_rates)
276
+ settlement = dc.convert_date(settlement)
277
+ maturities = pd.to_datetime(maturities, errors="coerce", dayfirst=True)
274
278
 
275
279
  # Create the interpolator object
276
280
  ytm_rate_interpolator = it.Interpolator(
277
281
  method="flat_forward",
278
- known_bdays=bday.count(settlement_date, ytm_rates.index),
279
- known_rates=ytm_rates,
282
+ known_bdays=bday.count(settlement, maturities),
283
+ known_rates=rates,
280
284
  )
281
285
 
282
- last_ntnb = ytm_rates.index.max()
286
+ last_ntnb = maturities.max()
283
287
 
284
288
  # Generate coupon dates up to the longest maturity date
285
- all_coupon_dates = _coupon_dates_map(start=settlement_date, end=last_ntnb)
289
+ all_coupon_dates = _coupon_dates_map(start=settlement, end=last_ntnb)
286
290
 
287
291
  # Create a DataFrame with all coupon dates and the corresponding YTM
288
292
  df_spot = pd.DataFrame(data=all_coupon_dates, columns=["MaturityDate"])
289
- df_spot["BDays"] = bday.count(settlement_date, df_spot["MaturityDate"])
293
+ df_spot["BDays"] = bday.count(settlement, df_spot["MaturityDate"])
290
294
  df_spot["YTM"] = df_spot["BDays"].apply(ytm_rate_interpolator)
291
295
 
292
296
  # The Bootstrap loop to calculate spot rates
@@ -297,45 +301,26 @@ def spot_rates(
297
301
  ytm = df_spot.at[index, "YTM"]
298
302
 
299
303
  # Get the coupon dates for the bond without the last one (principal + coupon)
300
- cp_dates = coupon_dates(settlement_date, maturity)
304
+ cp_dates = coupon_dates(settlement, maturity)
301
305
 
302
306
  # If there is only one coupon date and this date is the first maturity date
303
307
  # of an existing bond, the ytm rate is also a spot rate.
304
- if len(cp_dates) == 1 and cp_dates[0] == ytm_rates.index[0]:
308
+ if len(cp_dates) == 1 and cp_dates[0] == rates.index[0]:
305
309
  df_spot.at[index, "SpotRate"] = ytm
306
310
  continue
307
311
 
308
312
  # Calculate the real spot rate for the bond
309
- coupons_pv = _calculate_coupons_pv(df_spot, settlement_date, maturity)
310
- bond_price = quotation(settlement_date, maturity, ytm)
313
+ coupons_pv = _calculate_coupons_pv(df_spot, settlement, maturity)
314
+ bond_price = quotation(settlement, maturity, ytm)
311
315
  spot_rate = (FINAL_PMT / (bond_price - coupons_pv)) ** (252 / bdays) - 1
312
316
  df_spot.at[index, "SpotRate"] = spot_rate
313
317
 
314
318
  df_spot.drop(columns=["BDays"], inplace=True)
315
319
  # Return the result without the intermediate coupon dates (virtual bonds)
316
- existing_maturities = ytm_rates.index # noqa
320
+ existing_maturities = rates.index # noqa
317
321
  return df_spot.query("MaturityDate in @existing_maturities").reset_index(drop=True)
318
322
 
319
323
 
320
- def anbima_spot_rates(
321
- reference_date: str | pd.Timestamp,
322
- settlement_date: str | pd.Timestamp,
323
- ) -> pd.DataFrame:
324
- """
325
- Fetch the NTN-B Anbima indicative rates and calculate the spot rates for the bonds.
326
-
327
- Args:
328
- reference_date (str | pd.Timestamp): The reference date for fetching the data.
329
- settlement_date (str | pd.Timestamp): The reference date for settlement.
330
-
331
- Returns:
332
- pd.DataFrame: DataFrame containing the maturity dates and corresponding real
333
- spot rates.
334
- """
335
- ytm_rates = anbima_rates(reference_date)
336
- return spot_rates(settlement_date, ytm_rates)
337
-
338
-
339
324
  def _get_nir_df(reference_date: pd.Timestamp) -> pd.DataFrame:
340
325
  """
341
326
  Fetch the Nominal Interest Rate (NIR) data for NTN-B bonds.
@@ -355,8 +340,6 @@ def _get_nir_df(reference_date: pd.Timestamp) -> pd.DataFrame:
355
340
  "TickerSymbol",
356
341
  "ExpirationDate",
357
342
  "BDaysToExp",
358
- "CurrentAskRate",
359
- "CurrentBidRate",
360
343
  "NIR_DI",
361
344
  ]
362
345
  elif "SettlementRate" in df.columns:
@@ -369,7 +352,7 @@ def _get_nir_df(reference_date: pd.Timestamp) -> pd.DataFrame:
369
352
  "NIR_DI",
370
353
  ]
371
354
  else:
372
- raise ValueError("NIR data not found in the DataFrame.")
355
+ raise ValueError("DI data not available.")
373
356
 
374
357
  df = df[keep_cols].dropna(subset=["NIR_DI"])
375
358
 
@@ -392,8 +375,9 @@ def _get_nir_df(reference_date: pd.Timestamp) -> pd.DataFrame:
392
375
 
393
376
  def bei_rates(
394
377
  reference_date: str | pd.Timestamp,
395
- settlement_date: str | pd.Timestamp,
396
- ytm_rates: pd.Series,
378
+ settlement: str | pd.Timestamp,
379
+ rates: list[float] | pd.Series,
380
+ maturities: list[str | pd.Timestamp] | pd.Series,
397
381
  ) -> pd.DataFrame:
398
382
  """
399
383
  Calculate the Breakeven Inflation (BEI) for NTN-B bonds based on nominal and real
@@ -402,26 +386,27 @@ def bei_rates(
402
386
  Args:
403
387
  reference_date (str or pd.Timestamp): The reference date for fetching data and
404
388
  performing calculations.
405
- settlement_date (str or pd.Timestamp): The settlement date for the bonds.
406
- ytm_rates (pd.Series): A series of Yield to Maturity (YTM) rates corresponding
389
+ settlement (str or pd.Timestamp): The settlement date for the bonds.
390
+ rates (pd.Series): A series of Yield to Maturity (YTM) rates corresponding
407
391
  to the maturity dates of the bonds indexed by the maturity dates.
408
392
 
409
393
  Returns:
410
394
  pd.DataFrame: DataFrame containing the breakeven inflation rates.
411
395
 
412
396
  Returned columns:
413
- - MaturityDate: Maturity date of the bond.
397
+ - MaturityDate: Maturity date of the bonds
414
398
  - BDays: Number of business days from the settlement date to the maturity.
415
- - YTM: Yield to Maturity rate for the bond.
416
- - RIR: Real Interest Rate for the bond.
417
- - NIR_DI: Nominal Interest Rate for the bond.
418
- - NIR_PRE: Nominal Interest Rate for the bond with DI spread.
419
- - BEI_DI: Breakeven Inflation Rate for the bond.
420
- - BEI_PRE: Breakeven Inflation Rate for the bond adjusted for DI spread.
399
+ - YTM: Yield to Maturity rate for the bonds
400
+ - RIR: Real Interest Rate for the bonds based on the spot rates.
401
+ - NIR_DI: Nominal Interest Rate based on DI Futures.
402
+ - NIR_PRE: Nominal Interest Rate for the prefixed bonds.
403
+ - BEI_DI: Breakeven Inflation Rate calculated with DI Futures.
404
+ - BEI_PRE: Breakeven Inflation Rate calculated with prefixed bonds.
421
405
  """
422
406
  # Normalize input dates
423
407
  reference_date = dc.convert_date(reference_date)
424
- settlement_date = dc.convert_date(settlement_date)
408
+ settlement = dc.convert_date(settlement)
409
+ maturities = pd.to_datetime(maturities, errors="coerce", dayfirst=True)
425
410
 
426
411
  # Fetch Nominal Interest Rate (NIR) data
427
412
  df_nir = _get_nir_df(reference_date)
@@ -432,7 +417,7 @@ def bei_rates(
432
417
  known_rates=df_nir["NIR_DI"],
433
418
  )
434
419
  # Calculate Real Interest Rate (RIR)
435
- df = spot_rates(settlement_date, ytm_rates)
420
+ df = spot_rates(settlement, rates, maturities)
436
421
  df = df.rename(columns={"SpotRate": "RIR"})
437
422
  df["BDays"] = bday.count(reference_date, df["MaturityDate"])
438
423
  df["NIR_DI"] = df["BDays"].apply(ytm_interplator)
@@ -23,7 +23,7 @@ FINAL_PMT = 1048.80885
23
23
  di_data = di.DIData()
24
24
 
25
25
 
26
- def check_maturity_date(maturity_date: pd.Timestamp) -> None:
26
+ def check_maturity_date(maturity: pd.Timestamp) -> None:
27
27
  """
28
28
  Check if the maturity date is a valid NTN-F maturity date.
29
29
 
@@ -33,13 +33,13 @@ def check_maturity_date(maturity_date: pd.Timestamp) -> None:
33
33
  Raises:
34
34
  ValueError: If the maturity date is not the 1st of January.
35
35
  """
36
- if maturity_date.day != 1 or maturity_date.month not in COUPON_MONTHS:
36
+ if maturity.day != 1 or maturity.month not in COUPON_MONTHS:
37
37
  raise ValueError("NTN-F maturity date must be the 1st of January.")
38
38
 
39
39
 
40
40
  def coupon_dates(
41
- settlement_date: str | pd.Timestamp,
42
- maturity_date: str | pd.Timestamp,
41
+ settlement: str | pd.Timestamp,
42
+ maturity: str | pd.Timestamp,
43
43
  ) -> pd.Series:
44
44
  """
45
45
  Generate all remaining coupon dates between a settlement date and a maturity date.
@@ -48,29 +48,29 @@ def coupon_dates(
48
48
  The NTN-F bond is determined by its maturity date.
49
49
 
50
50
  Args:
51
- settlement_date (str | pd.Timestamp): The settlement date.
52
- maturity_date (str | pd.Timestamp): The maturity date.
51
+ settlement (str | pd.Timestamp): The settlement date.
52
+ maturity (str | pd.Timestamp): The maturity date.
53
53
 
54
54
  Returns:
55
55
  pd.Series: Series of coupon dates within the specified range.
56
56
  """
57
57
  # Validate and normalize dates
58
- settlement_date = dc.convert_date(settlement_date)
59
- maturity_date = dc.convert_date(maturity_date)
58
+ settlement = dc.convert_date(settlement)
59
+ maturity = dc.convert_date(maturity)
60
60
 
61
61
  # Check if the maturity date is valid
62
- check_maturity_date(maturity_date)
62
+ check_maturity_date(maturity)
63
63
 
64
64
  # Check if maturity date is after the start date
65
- if maturity_date <= settlement_date:
65
+ if maturity <= settlement:
66
66
  raise ValueError("Maturity date must be after the start date.")
67
67
 
68
68
  # Initialize loop variables
69
- coupon_date = maturity_date
69
+ coupon_date = maturity
70
70
  coupon_dates = []
71
71
 
72
72
  # Iterate backwards from the maturity date to the settlement date
73
- while coupon_date > settlement_date:
73
+ while coupon_date > settlement:
74
74
  coupon_dates.append(coupon_date)
75
75
  # Move the coupon date back 6 months
76
76
  coupon_date -= pd.DateOffset(months=6)
@@ -80,8 +80,8 @@ def coupon_dates(
80
80
 
81
81
 
82
82
  def cash_flows(
83
- settlement_date: str | pd.Timestamp,
84
- maturity_date: str | pd.Timestamp,
83
+ settlement: str | pd.Timestamp,
84
+ maturity: str | pd.Timestamp,
85
85
  ) -> pd.Series:
86
86
  """
87
87
  Generate the cash flows for the NTN-F bond between the settlement (exclusive) and
@@ -89,24 +89,24 @@ def cash_flows(
89
89
  payment at maturity.
90
90
 
91
91
  Args:
92
- settlement_date (str | pd.Timestamp): The settlement date in 'DD-MM-YYYY' format
92
+ settlement (str | pd.Timestamp): The settlement date in 'DD-MM-YYYY' format
93
93
  or a pandas Timestamp.
94
- maturity_date (str | pd.Timestamp): The maturity date in 'DD-MM-YYYY' format or
94
+ maturity (str | pd.Timestamp): The maturity date in 'DD-MM-YYYY' format or
95
95
  a pandas Timestamp.
96
96
 
97
97
  Returns:
98
98
  pd.Series: Series of cash flows within the specified range.
99
99
  """
100
100
  # Validate input dates
101
- settlement_date = dc.convert_date(settlement_date)
102
- maturity_date = dc.convert_date(maturity_date)
103
- check_maturity_date(maturity_date)
101
+ settlement = dc.convert_date(settlement)
102
+ maturity = dc.convert_date(maturity)
103
+ check_maturity_date(maturity)
104
104
 
105
105
  # Get the coupon payment dates between the settlement and maturity dates
106
- payment_dates = coupon_dates(settlement_date, maturity_date)
106
+ payment_dates = coupon_dates(settlement, maturity)
107
107
 
108
108
  # Set the cash flow at maturity to FINAL_PMT and the others to COUPON_PMT
109
- cfs = np.where(payment_dates == maturity_date, FINAL_PMT, COUPON_PMT)
109
+ cfs = np.where(payment_dates == maturity, FINAL_PMT, COUPON_PMT)
110
110
 
111
111
  df = pd.DataFrame(data=cfs, index=payment_dates, columns=["CashFlow"])
112
112
  df.index.name = "PaymentDate"
@@ -115,21 +115,21 @@ def cash_flows(
115
115
 
116
116
 
117
117
  def price(
118
- settlement_date: str | pd.Timestamp,
119
- maturity_date: str | pd.Timestamp,
120
- ytm_rate: float,
118
+ settlement: str | pd.Timestamp,
119
+ maturity: str | pd.Timestamp,
120
+ rate: float,
121
121
  ) -> float:
122
122
  """
123
123
  Calculate the NTN-F price using Anbima rules, which corresponds to the present
124
124
  value of the cash flows discounted at the given yield to maturity rate (YTM).
125
125
 
126
126
  Args:
127
- settlement_date (str | pd.Timestamp): The settlement date in 'DD-MM-YYYY' format
127
+ settlement (str | pd.Timestamp): The settlement date in 'DD-MM-YYYY' format
128
128
  or a pandas Timestamp.
129
- maturity_date (str | pd.Timestamp): The maturity date in 'DD-MM-YYYY' format or
129
+ maturity (str | pd.Timestamp): The maturity date in 'DD-MM-YYYY' format or
130
130
  a pandas Timestamp.
131
- ytm_rate (float): The discount rate used to calculate the present value of
132
- the cash flows.
131
+ rate (float): The discount rate (yield to maturity) used to calculate the
132
+ present value of the cash flows.
133
133
 
134
134
  Returns:
135
135
  float: The NTN-F price using Anbima rules.
@@ -144,17 +144,32 @@ def price(
144
144
  >>> price("05-07-2024", "01-01-2035", 0.11921)
145
145
  895.359254
146
146
  """
147
- df_cf = cash_flows(settlement_date, maturity_date).reset_index()
147
+ df_cf = cash_flows(settlement, maturity).reset_index()
148
148
  cfs = df_cf["CashFlow"]
149
- bdays = bday.count(settlement_date, df_cf["PaymentDate"])
149
+ bdays = bday.count(settlement, df_cf["PaymentDate"])
150
150
  byears = ut.truncate(bdays / 252, 14)
151
- discount_factors = (1 + ytm_rate) ** byears
151
+ discount_factors = (1 + rate) ** byears
152
152
  # Calculate the present value of each cash flow (DCF) rounded as per Anbima rules
153
153
  dcf = (cfs / discount_factors).round(9)
154
154
  # Return the sum of the discounted cash flows truncated as per Anbima rules
155
155
  return ut.truncate(dcf.sum(), 6)
156
156
 
157
157
 
158
+ def maturities(reference_date: str | pd.Timestamp) -> list[pd.Timestamp]:
159
+ """
160
+ Fetch the NTN-F bond maturities available for the given reference date.
161
+
162
+ Args:
163
+ reference_date (str | pd.Timestamp): The reference date for fetching the data.
164
+
165
+ Returns:
166
+ list[pd.Timestamp]: A list of NTN-F bond maturities available for the reference
167
+ date.
168
+ """
169
+ rates = an.get_anbima_rates(reference_date, "NTN-F")
170
+ return rates.index.to_list()
171
+
172
+
158
173
  def anbima_data(reference_date: str | pd.Timestamp) -> pd.DataFrame:
159
174
  """
160
175
  Fetch NTN-F Anbima data for the given reference date.
@@ -170,23 +185,24 @@ def anbima_data(reference_date: str | pd.Timestamp) -> pd.DataFrame:
170
185
 
171
186
  def indicative_rates(
172
187
  reference_date: str | pd.Timestamp,
173
- maturity_date: str | pd.Timestamp | None = None,
188
+ maturity: str | pd.Timestamp | None = None,
174
189
  ) -> pd.Series | float:
175
190
  """
176
- Fetch NTN-F Anbima indicative rates for the given reference date.
191
+ Fetch NTN-F Anbima indicative rates for the given reference date. If a maturity date
192
+ is provided, the function returns the rate for the given maturity date.
177
193
 
178
194
  Args:
179
195
  reference_date (str | pd.Timestamp): The reference date for fetching the data.
196
+ maturity_date (str | pd.Timestamp, optional): The maturity date of the bond.
180
197
 
181
198
  Returns:
182
199
  pd.Series: A Series containing the rates indexed by maturity date.
183
200
  """
184
201
  rates = an.get_anbima_rates(reference_date, "NTN-F")
185
-
186
- if maturity_date:
187
- maturity_date = dc.convert_date(maturity_date)
188
- if maturity_date in rates.index:
189
- return float(rates[maturity_date])
202
+ if maturity:
203
+ maturity = dc.convert_date(maturity)
204
+ if maturity in rates.index:
205
+ return float(rates[maturity])
190
206
  else:
191
207
  return float("nan")
192
208
  return rates
@@ -207,27 +223,29 @@ def anbima_historical_rates(maturity_date: str | pd.Timestamp) -> pd.Series:
207
223
 
208
224
  def _calculate_coupons_pv(
209
225
  bootstrap_df: pd.DataFrame,
210
- settlement_date: pd.Timestamp,
211
- maturity_date: pd.Timestamp,
226
+ settlement: pd.Timestamp,
227
+ maturity: pd.Timestamp,
212
228
  ) -> float:
213
229
  # Create a subset DataFrame with only the coupon payments (without last payment)
214
- cp_dates_wo_last = coupon_dates(settlement_date, maturity_date)[:-1] # noqa
230
+ cp_dates_wo_last = coupon_dates(settlement, maturity)[:-1] # noqa
215
231
  df_coupons = bootstrap_df.query("MaturityDate in @cp_dates_wo_last").copy()
216
232
  df_coupons["Coupon"] = COUPON_PMT
217
233
 
218
234
  # Calculate the present value of the coupon payments
219
235
  pv = ut.calculate_present_value(
220
236
  cash_flows=df_coupons["Coupon"],
221
- discount_rates=df_coupons["SpotRate"],
222
- time_periods=df_coupons["BDays"] / 252,
237
+ rates=df_coupons["SpotRate"],
238
+ periods=df_coupons["BDays"] / 252,
223
239
  )
224
240
  return pv
225
241
 
226
242
 
227
243
  def spot_rates(
228
- settlement_date: str | pd.Timestamp,
229
- ltn_rates: pd.Series,
230
- ntnf_rates: pd.Series,
244
+ settlement: str | pd.Timestamp,
245
+ ltn_rates: list[float] | pd.Series,
246
+ ltn_maturities: list[pd.Timestamp] | pd.Series,
247
+ ntnf_rates: list[float] | pd.Series,
248
+ ntnf_maturities: list[pd.Timestamp] | pd.Series,
231
249
  ) -> pd.DataFrame:
232
250
  """
233
251
  Calculate the spot rates for NTN-F bonds using the bootstrap method.
@@ -249,32 +267,32 @@ def spot_rates(
249
267
  the corresponding spot rates.
250
268
  """
251
269
  # Process and validate the input data
252
- settlement_date = dc.convert_date(settlement_date)
253
- ltn_rates = ut.standardize_rates(ltn_rates)
254
- ntnf_rates = ut.standardize_rates(ntnf_rates)
270
+ settlement = dc.convert_date(settlement)
271
+ ltn_maturities = pd.to_datetime(ltn_maturities, errors="coerce", dayfirst=True)
272
+ ntnf_maturities = pd.to_datetime(ntnf_maturities, errors="coerce", dayfirst=True)
255
273
 
256
274
  # Create flat forward interpolators for LTN and NTN-F rates
257
275
  ltn_rate_interpolator = Interpolator(
258
276
  method="flat_forward",
259
- known_bdays=bday.count(settlement_date, ltn_rates.index),
277
+ known_bdays=bday.count(settlement, ltn_maturities),
260
278
  known_rates=ltn_rates,
261
279
  )
262
280
  ntnf_rate_interpolator = Interpolator(
263
281
  method="flat_forward",
264
- known_bdays=bday.count(settlement_date, ntnf_rates.index),
282
+ known_bdays=bday.count(settlement, ntnf_maturities),
265
283
  known_rates=ntnf_rates,
266
284
  )
267
285
 
268
286
  # Determine the last maturity dates for LTN and NTN-F rates
269
- last_ltn = ltn_rates.index.max()
270
- last_ntnf = ntnf_rates.index.max()
287
+ last_ltn = ltn_maturities.max()
288
+ last_ntnf = ntnf_maturities.max()
271
289
 
272
290
  # Generate all coupon dates up to the last NTN-F maturity date
273
- all_coupon_dates = coupon_dates(settlement_date, last_ntnf)
291
+ all_coupon_dates = coupon_dates(settlement, last_ntnf)
274
292
 
275
293
  # Create a DataFrame with all coupon dates and the corresponding YTM
276
294
  df_spot = pd.DataFrame(data=all_coupon_dates, columns=["MaturityDate"])
277
- df_spot["BDays"] = bday.count(start=settlement_date, end=df_spot["MaturityDate"])
295
+ df_spot["BDays"] = bday.count(start=settlement, end=df_spot["MaturityDate"])
278
296
  df_spot["YTM"] = df_spot["BDays"].apply(ntnf_rate_interpolator)
279
297
 
280
298
  # The Bootstrap loop to calculate spot rates
@@ -288,16 +306,16 @@ def spot_rates(
288
306
  continue
289
307
 
290
308
  # Calculate the spot rate for the bond
291
- coupons_pv = _calculate_coupons_pv(df_spot, settlement_date, maturity)
309
+ coupons_pv = _calculate_coupons_pv(df_spot, settlement, maturity)
292
310
  ytm = df_spot.at[index, "YTM"]
293
- bond_price = price(settlement_date, maturity, ytm)
311
+ bond_price = price(settlement, maturity, ytm)
294
312
  spot_rate = (FINAL_PMT / (bond_price - coupons_pv)) ** (252 / bdays) - 1
295
313
  df_spot.at[index, "SpotRate"] = spot_rate
296
314
 
297
315
  return df_spot
298
316
 
299
317
 
300
- def gross_di_spreads(reference_date: str | pd.Timestamp) -> pd.Series:
318
+ def di_gross_spreads(reference_date: str | pd.Timestamp) -> pd.Series:
301
319
  """
302
320
  Calculates the DI spread for the NTN-F based on ANBIMA's indicative rates.
303
321
 
@@ -321,9 +339,9 @@ def gross_di_spreads(reference_date: str | pd.Timestamp) -> pd.Series:
321
339
  return df["DISpread"]
322
340
 
323
341
 
324
- def net_di_spread( # noqa
325
- settlement_date: str | pd.Timestamp,
326
- maturity_date: str | pd.Timestamp,
342
+ def di_net_spread( # noqa
343
+ settlement: str | pd.Timestamp,
344
+ maturity: str | pd.Timestamp,
327
345
  ytm: float,
328
346
  di_rates: list[float] | pd.Series,
329
347
  di_expirations: list[pd.Timestamp] | pd.Series,
@@ -338,47 +356,45 @@ def net_di_spread( # noqa
338
356
  (in bps) that zeroes the difference between the bond's market price and its
339
357
  discounted cash flows.
340
358
 
341
- Parameters:
342
- ----------
343
- settlement_date : pd.Timestamp
344
- The date on which the bond is settled.
345
- maturity_date : pd.Timestamp
346
- The date on which the bond matures.
347
- ytm : float
348
- The yield to maturity of the bond.
349
- di_rates : list[float]
350
- A list of DI rates corresponding to the DI expirations.
351
- di_expirations : list[pd.Timestamp]
352
- A list of DI expiration dates.
359
+ Args:
360
+ settlement (str | pd.Timestamp): The settlement date in 'DD-MM-YYYY' format
361
+ or a pandas Timestamp.
362
+ maturity (str | pd.Timestamp): The maturity date in 'DD-MM-YYYY'
363
+ format or a pandas Timestamp.
364
+ ytm (float): The yield to maturity (YTM) of the bond.
365
+ di_rates (list[float] | pd.Series): A list or Series of DI rates.
366
+ di_expirations (list[pd.Timestamp] | pd.Series): A list or Series of DI
367
+ expiration dates.
368
+ initial_guess (float, optional): An initial guess for the spread.
369
+ Defaults to None.
353
370
 
354
371
  Returns:
355
- -------
356
- float
357
- The net DI spread in basis points. Returns NaN if a solution cannot be found.
372
+ float: The net DI spread in basis points.
358
373
  """
359
-
360
374
  # Create an interpolator for the DI rates using the flat-forward method
361
- settlement_date = dc.convert_date(settlement_date)
362
- maturity_date = dc.convert_date(maturity_date)
375
+ settlement = dc.convert_date(settlement)
376
+ maturity = dc.convert_date(maturity)
363
377
 
364
378
  ff_interpolator = Interpolator(
365
379
  "flat_forward",
366
- bday.count(settlement_date, di_expirations),
380
+ bday.count(settlement, di_expirations),
367
381
  di_rates,
368
382
  )
369
383
 
370
- # Ensure the DI rates and expirations lists are of equal length
384
+ # Ensure the DI data is valid
371
385
  if len(di_rates) != len(di_expirations):
372
386
  raise ValueError("di_rates and di_expirations must have the same length.")
387
+ if len(di_rates) == 0:
388
+ return float("nan")
373
389
 
374
390
  # Calculate cash flows and business days between settlement and payment dates
375
- df = cash_flows(settlement_date, maturity_date).reset_index()
376
- df["BDays"] = bday.count(settlement_date, df["PaymentDate"])
391
+ df = cash_flows(settlement, maturity).reset_index()
392
+ df["BDays"] = bday.count(settlement, df["PaymentDate"])
377
393
 
378
394
  # Calculate business years (252 business days per year)
379
- byears = bday.count(settlement_date, df["PaymentDate"]) / 252
395
+ byears = bday.count(settlement, df["PaymentDate"]) / 252
380
396
  di_interp = df["BDays"].apply(ff_interpolator)
381
- bond_price = price(settlement_date, maturity_date, ytm)
397
+ bond_price = price(settlement, maturity, ytm)
382
398
  bond_cash_flows = df["CashFlow"]
383
399
 
384
400
  def price_difference(p):
@@ -36,16 +36,10 @@ def truncate(values: float | pd.Series, decimal_places: int) -> float | pd.Serie
36
36
 
37
37
  def calculate_present_value(
38
38
  cash_flows: pd.Series,
39
- discount_rates: pd.Series,
40
- time_periods: pd.Series,
39
+ rates: pd.Series,
40
+ periods: pd.Series,
41
41
  ) -> float:
42
- return (cash_flows / (1 + discount_rates) ** time_periods).sum()
43
-
44
-
45
- def standardize_rates(rates: pd.Series) -> pd.Series:
46
- if not isinstance(rates.index, pd.DatetimeIndex):
47
- raise ValueError("The rates index must be a DatetimeIndex with maturity dates.")
48
- return rates.dropna().sort_index()
42
+ return (cash_flows / (1 + rates) ** periods).sum()
49
43
 
50
44
 
51
45
  def di_spreads(reference_date: pd.Timestamp) -> pd.DataFrame:
@@ -1,3 +1,5 @@
1
+ from typing import Literal
2
+
1
3
  import pandas as pd
2
4
 
3
5
  from . import bday
@@ -9,6 +11,11 @@ DI_URL = "https://raw.githubusercontent.com/crdcj/pyield-data/main/di_data.parqu
9
11
  class DIData:
10
12
  _df = pd.DataFrame()
11
13
 
14
+ def __init__(self):
15
+ """Initialize the DIData class and load the data."""
16
+ if self._df.empty or not self._is_data_up_to_date():
17
+ self._load_data()
18
+
12
19
  @classmethod
13
20
  def _load_data(cls):
14
21
  print("Loading DI dataset...")
@@ -36,19 +43,31 @@ class DIData:
36
43
  cls._load_data()
37
44
  return cls._df.copy()
38
45
 
46
+ @staticmethod
47
+ def _get_rate_column(rate_type: str) -> str:
48
+ rate_map = {
49
+ "settlement": "SettlementRate",
50
+ "min": "MinRate",
51
+ "max": "MaxRate",
52
+ "close": "CloseRate",
53
+ }
54
+ if rate_type not in rate_map:
55
+ raise ValueError(
56
+ "Invalid rate type. Use 'settlement', 'min', 'max', or 'close'."
57
+ )
58
+ return rate_map[rate_type]
59
+
39
60
  @classmethod
40
- def settlement_rates(
61
+ def rates(
41
62
  cls,
42
63
  trade_date: str | pd.Timestamp | None = None,
43
64
  expiration_date: str | pd.Timestamp | None = None,
65
+ rate_type: Literal["settlement", "min", "max", "close"] = "settlement",
44
66
  adjust_exp_date: bool = False,
45
- ) -> pd.DataFrame:
67
+ ) -> pd.DataFrame | float:
68
+ rate_col = cls._get_rate_column(rate_type)
46
69
  cls._check_for_updates()
47
- df = cls._df[["TradeDate", "ExpirationDate", "SettlementRate"]].copy()
48
-
49
- if trade_date:
50
- trade_date = dc.convert_date(trade_date)
51
- df.query("TradeDate == @trade_date", inplace=True)
70
+ df = cls._df[["TradeDate", "ExpirationDate", f"{rate_col}"]].copy()
52
71
 
53
72
  if expiration_date:
54
73
  expiration_date = dc.convert_date(expiration_date)
@@ -56,28 +75,16 @@ class DIData:
56
75
  expiration_date = bday.offset(expiration_date, 0)
57
76
  df.query("ExpirationDate == @expiration_date", inplace=True)
58
77
 
59
- if adjust_exp_date:
60
- df["ExpirationDate"] = (
61
- df["ExpirationDate"].dt.to_period("M").dt.to_timestamp()
62
- )
63
-
64
- return df.sort_values(["TradeDate", "ExpirationDate"]).reset_index(drop=True)
65
-
66
- @classmethod
67
- def settlement_rate(
68
- cls,
69
- trade_date: str | pd.Timestamp,
70
- expiration_date: str | pd.Timestamp,
71
- ) -> float:
72
- cls._check_for_updates()
73
- df = cls._df.copy()
74
-
75
- trade_date = dc.convert_date(trade_date)
76
- df.query("TradeDate == @trade_date", inplace=True)
77
-
78
- expiration_date = dc.convert_date(expiration_date)
79
- # Force the expiration date to be a business day
80
- expiration_date = bday.offset(expiration_date, 0)
81
- df.query("ExpirationDate == @expiration_date", inplace=True)
78
+ if trade_date:
79
+ trade_date = dc.convert_date(trade_date)
80
+ df.query("TradeDate == @trade_date", inplace=True)
82
81
 
83
- return float(df["SettlementRate"].iat[0])
82
+ if adjust_exp_date:
83
+ df["ExpirationDate"] = df["ExpirationDate"].dt.to_period("M")
84
+ df["ExpirationDate"] = df["ExpirationDate"].dt.to_timestamp()
85
+
86
+ df.sort_values(["TradeDate", "ExpirationDate"]).reset_index(drop=True)
87
+ if len(df.index) == 1:
88
+ return float(df[f"{rate_col}"].values[0])
89
+ else:
90
+ return df
@@ -13,7 +13,7 @@ ANBIMA_URL = "https://www.anbima.com.br/informacoes/merc-sec/arqs"
13
13
  ANBIMA_MEMBER_URL = "http://www.anbima.associados.rtm/merc_sec/arqs"
14
14
  # URL example: https://www.anbima.com.br/informacoes/merc-sec/arqs/ms240614.txt
15
15
  RATES_URL = (
16
- "https://raw.githubusercontent.com/crdcj/pyield-data/main/anbima_rates.parquet"
16
+ "https://raw.githubusercontent.com/crdcj/pyield-data/main/anbima_data.parquet"
17
17
  )
18
18
 
19
19
  # Before 13/05/2014 the file was zipped and the endpoint ended with ".exe"
@@ -157,9 +157,8 @@ class RatesData:
157
157
  @classmethod
158
158
  def _load_data(cls):
159
159
  # if cls._df.empty or not cls._is_data_up_to_date():
160
- print("Loading ANBIMA rates data...")
160
+ print("Loading ANBIMA data...")
161
161
  cls._df = pd.read_parquet(RATES_URL)
162
- cls._df["IndicativeRate"] = (cls._df["IndicativeRate"] / 100).round(6)
163
162
  cls._last_update = pd.Timestamp.today().normalize()
164
163
 
165
164
  @classmethod
@@ -34,7 +34,7 @@ dependencies = [
34
34
  "pyarrow",
35
35
  ]
36
36
  dynamic = []
37
- version = "0.12.2"
37
+ version = "0.13.0"
38
38
 
39
39
  [project.license]
40
40
  file = "LICENSE"
@@ -1 +0,0 @@
1
- __version__ = "0.12.2"
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