PYield 0.37.2__tar.gz → 0.37.3__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 (53) hide show
  1. {pyield-0.37.2 → pyield-0.37.3}/PKG-INFO +1 -1
  2. pyield-0.37.3/pyield/__about__.py +1 -0
  3. {pyield-0.37.2 → pyield-0.37.3}/pyield/anbima/__init__.py +2 -2
  4. {pyield-0.37.2 → pyield-0.37.3}/pyield/anbima/tpf.py +96 -94
  5. pyield-0.37.2/pyield/__about__.py +0 -1
  6. {pyield-0.37.2 → pyield-0.37.3}/.gitignore +0 -0
  7. {pyield-0.37.2 → pyield-0.37.3}/LICENSE +0 -0
  8. {pyield-0.37.2 → pyield-0.37.3}/README.md +0 -0
  9. {pyield-0.37.2 → pyield-0.37.3}/pyield/__init__.py +0 -0
  10. {pyield-0.37.2 → pyield-0.37.3}/pyield/anbima/ettj.py +0 -0
  11. {pyield-0.37.2 → pyield-0.37.3}/pyield/anbima/ima.py +0 -0
  12. {pyield-0.37.2 → pyield-0.37.3}/pyield/anbima/imaq.py +0 -0
  13. {pyield-0.37.2 → pyield-0.37.3}/pyield/anbima/ipca.py +0 -0
  14. {pyield-0.37.2 → pyield-0.37.3}/pyield/b3/__init__.py +0 -0
  15. {pyield-0.37.2 → pyield-0.37.3}/pyield/b3/di1.py +0 -0
  16. {pyield-0.37.2 → pyield-0.37.3}/pyield/b3/di_over.py +0 -0
  17. {pyield-0.37.2 → pyield-0.37.3}/pyield/b3/futures/__init__.py +0 -0
  18. {pyield-0.37.2 → pyield-0.37.3}/pyield/b3/futures/common.py +0 -0
  19. {pyield-0.37.2 → pyield-0.37.3}/pyield/b3/futures/historical.py +0 -0
  20. {pyield-0.37.2 → pyield-0.37.3}/pyield/b3/futures/intraday.py +0 -0
  21. {pyield-0.37.2 → pyield-0.37.3}/pyield/b3/futures/xml.py +0 -0
  22. {pyield-0.37.2 → pyield-0.37.3}/pyield/bc/__init__.py +0 -0
  23. {pyield-0.37.2 → pyield-0.37.3}/pyield/bc/auction.py +0 -0
  24. {pyield-0.37.2 → pyield-0.37.3}/pyield/bc/ptax_api.py +0 -0
  25. {pyield-0.37.2 → pyield-0.37.3}/pyield/bc/rates.py +0 -0
  26. {pyield-0.37.2 → pyield-0.37.3}/pyield/bc/repo.py +0 -0
  27. {pyield-0.37.2 → pyield-0.37.3}/pyield/bc/trades_intraday.py +0 -0
  28. {pyield-0.37.2 → pyield-0.37.3}/pyield/bc/trades_monthly.py +0 -0
  29. {pyield-0.37.2 → pyield-0.37.3}/pyield/bc/vna.py +0 -0
  30. {pyield-0.37.2 → pyield-0.37.3}/pyield/bday.py +0 -0
  31. {pyield-0.37.2 → pyield-0.37.3}/pyield/conftest.py +0 -0
  32. {pyield-0.37.2 → pyield-0.37.3}/pyield/data_cache.py +0 -0
  33. {pyield-0.37.2 → pyield-0.37.3}/pyield/date_converter.py +0 -0
  34. {pyield-0.37.2 → pyield-0.37.3}/pyield/fwd.py +0 -0
  35. {pyield-0.37.2 → pyield-0.37.3}/pyield/holidays/__init__.py +0 -0
  36. {pyield-0.37.2 → pyield-0.37.3}/pyield/holidays/br_holidays_new.txt +0 -0
  37. {pyield-0.37.2 → pyield-0.37.3}/pyield/holidays/br_holidays_old.txt +0 -0
  38. {pyield-0.37.2 → pyield-0.37.3}/pyield/holidays/brholidays.py +0 -0
  39. {pyield-0.37.2 → pyield-0.37.3}/pyield/ibge/__init__.py +0 -0
  40. {pyield-0.37.2 → pyield-0.37.3}/pyield/ibge/ipca.py +0 -0
  41. {pyield-0.37.2 → pyield-0.37.3}/pyield/interpolator.py +0 -0
  42. {pyield-0.37.2 → pyield-0.37.3}/pyield/py.typed +0 -0
  43. {pyield-0.37.2 → pyield-0.37.3}/pyield/retry.py +0 -0
  44. {pyield-0.37.2 → pyield-0.37.3}/pyield/tn/__init__.py +0 -0
  45. {pyield-0.37.2 → pyield-0.37.3}/pyield/tn/benchmark.py +0 -0
  46. {pyield-0.37.2 → pyield-0.37.3}/pyield/tn/lft.py +0 -0
  47. {pyield-0.37.2 → pyield-0.37.3}/pyield/tn/ltn.py +0 -0
  48. {pyield-0.37.2 → pyield-0.37.3}/pyield/tn/ntnb.py +0 -0
  49. {pyield-0.37.2 → pyield-0.37.3}/pyield/tn/ntnc.py +0 -0
  50. {pyield-0.37.2 → pyield-0.37.3}/pyield/tn/ntnf.py +0 -0
  51. {pyield-0.37.2 → pyield-0.37.3}/pyield/tn/pre.py +0 -0
  52. {pyield-0.37.2 → pyield-0.37.3}/pyield/tn/tools.py +0 -0
  53. {pyield-0.37.2 → pyield-0.37.3}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PYield
3
- Version: 0.37.2
3
+ Version: 0.37.3
4
4
  Summary: A Python library for analysis of fixed income instruments in Brazil
5
5
  Project-URL: Source, https://github.com/crdcj/PYield
6
6
  Author-email: Carlos Carvalho <cr.cj@outlook.com>
@@ -0,0 +1 @@
1
+ __version__ = "0.37.3"
@@ -2,13 +2,13 @@ from pyield.anbima.ettj import intraday_ettj, last_ettj
2
2
  from pyield.anbima.ima import last_ima
3
3
  from pyield.anbima.imaq import imaq
4
4
  from pyield.anbima.ipca import ipca_projection
5
- from pyield.anbima.tpf import fetch_tpf_data, tpf_data, tpf_fixed_rate_maturities
5
+ from pyield.anbima.tpf import _fetch_tpf_data, tpf_data, tpf_fixed_rate_maturities
6
6
 
7
7
  __all__ = [
8
8
  "last_ima",
9
9
  "imaq",
10
10
  "ipca_projection",
11
- "fetch_tpf_data",
11
+ "_fetch_tpf_data",
12
12
  "tpf_data",
13
13
  "tpf_fixed_rate_maturities",
14
14
  "last_ettj",
@@ -8,6 +8,7 @@ from zoneinfo import ZoneInfo
8
8
  import pandas as pd
9
9
 
10
10
  from pyield import bday
11
+ from pyield.b3 import di1
11
12
  from pyield.bc.ptax_api import ptax
12
13
  from pyield.data_cache import get_cached_dataset
13
14
  from pyield.date_converter import DateScalar, convert_input_dates
@@ -106,7 +107,7 @@ def _process_raw_df(df_raw: pd.DataFrame) -> pd.DataFrame:
106
107
  return df
107
108
 
108
109
 
109
- def _process_dv01(df: pd.DataFrame) -> pd.DataFrame:
110
+ def _add_dv01(df: pd.DataFrame) -> pd.DataFrame:
110
111
  df["BDToMat"] = bday.count(start=df["ReferenceDate"], end=df["MaturityDate"])
111
112
 
112
113
  df["Duration"] = 0.0
@@ -167,6 +168,17 @@ def _add_usd_dv01(df: pd.DataFrame) -> pd.DataFrame:
167
168
  return df
168
169
 
169
170
 
171
+ def _add_di_rate(df: pd.DataFrame, target_date: pd.Timestamp) -> pd.DataFrame:
172
+ """Add the DI rate column to the DataFrame."""
173
+ # INSERIR OS DADOS DO DI INTERPOLADO ###
174
+ df["DIRate"] = di1.interpolate_rates(
175
+ dates=target_date,
176
+ expirations=df["MaturityDate"],
177
+ extrapolate=True,
178
+ )
179
+ return df
180
+
181
+
170
182
  def _reorder_columns(df: pd.DataFrame) -> pd.DataFrame:
171
183
  """Reorder the columns of the DataFrame according to the specified order."""
172
184
  column_order = [
@@ -183,6 +195,7 @@ def _reorder_columns(df: pd.DataFrame) -> pd.DataFrame:
183
195
  "BidRate",
184
196
  "AskRate",
185
197
  "IndicativeRate",
198
+ "DIRate",
186
199
  "StdDev",
187
200
  "LowerBoundRateD0",
188
201
  "UpperBoundRateD0",
@@ -194,65 +207,22 @@ def _reorder_columns(df: pd.DataFrame) -> pd.DataFrame:
194
207
  return df[column_order].copy()
195
208
 
196
209
 
197
- def _select_main_columns(df: pd.DataFrame) -> pd.DataFrame:
198
- """Select only the main columns from the DataFrame."""
199
- main_columns = [
200
- "BondType",
201
- "ReferenceDate",
202
- "MaturityDate",
203
- "BDToMat",
204
- "Duration",
205
- "DV01",
206
- "DV01USD",
207
- "Price",
208
- "BidRate",
209
- "AskRate",
210
- "IndicativeRate",
211
- ]
212
- main_columns = [col for col in main_columns if col in df.columns]
213
- return df[main_columns].copy()
214
-
215
-
216
- def fetch_tpf_data(
217
- date: DateScalar,
218
- bond_type: str | BOND_TYPES | None = None,
219
- all_columns: bool = True,
220
- ) -> pd.DataFrame:
221
- """Fetch and process TPF secondary market data directly from the ANBIMA website.
222
- This is a low-level function intended for internal use or specific backend
223
- jobs. For general use, prefer `tpf_data` which includes caching logic.
224
-
225
- Only the last 5 days of data are available in the ANBIMA website. Data older
226
- than 5 business days is only available through the RTM network.
210
+ def _fetch_tpf_data(date: pd.Timestamp) -> pd.DataFrame:
211
+ """Fetch and process TPF secondary market data directly from the ANBIMA source.
227
212
 
228
- This function retrieves bond market data from the ANBIMA website for a
229
- specified reference date. It handles different file formats based on the date
230
- and attempts to download the data from both member and non-member URLs.
213
+ This is a low-level function intended for internal use. It handles the logic
214
+ of building the correct URL (public or RTM), downloading the data with retries,
215
+ and processing it into a structured DataFrame.
231
216
 
232
217
  Args:
233
- date (DateScalar): The reference date for the data.
234
- bond_type (str, optional): Filter data by bond type.
235
- Defaults to None, which returns data for all bond types.
236
- all_columns (bool, optional): If True, all columns are returned.
237
- Defaults to True. If False, only the main columns are returned.
218
+ date (pd.Timestamp): The reference date for the data.
238
219
 
239
220
  Returns:
240
- pd.DataFrame: A DataFrame containing bond market data.
241
- Returns an empty DataFrame if data is not available for the
242
- specified date (weekends, holidays, unavailable data, or lack of
243
- RTM access for older data).
221
+ pd.DataFrame: A DataFrame containing processed bond market data, or an
222
+ empty DataFrame if data is unavailable or a connection error occurs.
244
223
  """
245
- date = convert_input_dates(date)
246
- date_log = date.strftime("%d/%m/%Y")
247
- today = dt.datetime.now(BZ_TIMEZONE).date()
248
- if date.date() > today:
249
- logger.info(
250
- f"Cannot fetch data for a future date ({date_log}). "
251
- "Returning empty DataFrame."
252
- )
253
- return pd.DataFrame()
254
-
255
224
  file_url = _build_file_url(date)
225
+ date_str = date.strftime("%d/%m/%Y")
256
226
 
257
227
  # --- "FAIL-FAST" PARA EVITAR RETRIES DESNECESSÁRIOS NA RTM ---
258
228
  if ANBIMA_RTM_URL in file_url:
@@ -263,7 +233,7 @@ def fetch_tpf_data(
263
233
  # Se falhar (gaierror = get address info error), não estamos na RTM.
264
234
  # Não adianta prosseguir para a função com retry.
265
235
  logger.warning(
266
- f"Could not resolve RTM host for {date_log}. This is expected if "
236
+ f"Could not resolve RTM host for {date_str}. This is expected if "
267
237
  "you are not on the RTM network. Historical data requires RTM access. "
268
238
  "Returning empty DataFrame."
269
239
  )
@@ -274,40 +244,35 @@ def fetch_tpf_data(
274
244
  df = _read_raw_df(file_url)
275
245
  if df.empty:
276
246
  logger.info(
277
- f"Anbima TPF secondary market data for {date_log} not available. "
247
+ f"Anbima TPF secondary market data for {date_str} not available. "
278
248
  "Returning empty DataFrame."
279
249
  )
280
250
  return df
281
251
  df = _process_raw_df(df)
282
- df = _process_dv01(df)
252
+ df = _add_dv01(df)
283
253
  df = _add_usd_dv01(df)
254
+ df = _add_di_rate(df, target_date=date)
284
255
  df = _reorder_columns(df)
285
256
 
286
- if not all_columns:
287
- df = _select_main_columns(df)
288
-
289
- if bond_type:
290
- norm_bond_type = _bond_type_mapping(bond_type) # noqa
291
- df = df.query("BondType == @norm_bond_type").reset_index(drop=True)
292
257
  return df.sort_values(["BondType", "MaturityDate"]).reset_index(drop=True)
293
258
 
294
259
  except HTTPError as e:
295
260
  if e.code == 404: # noqa
296
261
  logger.info(
297
- f"No Anbima TPF secondary market data for {date_log} (HTTP 404). "
262
+ f"No Anbima TPF secondary market data for {date_str} (HTTP 404). "
298
263
  "Returning empty DataFrame."
299
264
  )
300
265
  return pd.DataFrame()
301
- logger.error(f"HTTP Error fetching data for {date_log} from {file_url}: {e}")
266
+ logger.error(f"HTTP Error fetching data for {date_str} from {file_url}: {e}")
302
267
  raise
303
268
 
304
269
  # Este bloco ainda é útil para outros URLErrors (ex: timeout genuíno na URL pública)
305
270
  except URLError:
306
- logger.exception(f"Network error (URLError) fetching TPF data for {date_log}")
271
+ logger.exception(f"Network error (URLError) fetching TPF data for {date_str}")
307
272
  raise
308
273
 
309
274
  except Exception:
310
- msg = f"An unexpected error occurred fetching TPF data for {date_log}"
275
+ msg = f"An unexpected error occurred fetching TPF data for {date_str}"
311
276
  logger.exception(msg)
312
277
  raise
313
278
 
@@ -315,27 +280,27 @@ def fetch_tpf_data(
315
280
  def tpf_data(
316
281
  date: DateScalar,
317
282
  bond_type: str | None = None,
318
- adj_maturities: bool = False,
283
+ fetch_from_source: bool = False,
319
284
  ) -> pd.DataFrame:
320
- """Retrieve indicative rates for bonds from ANBIMA data.
285
+ """Retrieve TPF secondary market data from ANBIMA.
321
286
 
322
- This function fetches indicative rates for bonds from ANBIMA,
323
- initially attempting to retrieve data from a cached dataset. If the data
324
- is not available in the cache, it fetches it directly from the ANBIMA website.
287
+ This function fetches indicative rates and other data for Brazilian government
288
+ bonds from ANBIMA. By default, it attempts to retrieve data from a local
289
+ cache for performance. If `fetch_from_source` is True, it tries to fetch the data
290
+ directly from the ANBIMA website.
325
291
 
326
292
  Args:
327
- date (DateScalar): The reference date for the rates.
328
- bond_type (str, optional): Filter rates by bond type.
329
- Defaults to None, which returns rates for all bond types.
330
- adj_maturities (bool, optional): Adjust maturity dates to the next
331
- business day. Defaults to False.
293
+ date (DateScalar): The reference date for the data (e.g., '2024-06-14').
294
+ bond_type (str, optional): Filters results by a specific bond type
295
+ (e.g., 'LTN', 'NTN-B'). Defaults to None, returning all bond types.
296
+ fetch_from_source (bool, optional): If True, forces the function to
297
+ bypass the cache and fetch data directly from the source.
298
+ Defaults to False.
332
299
 
333
300
  Returns:
334
- pd.DataFrame: A DataFrame with the following columns:
335
- - BondType: The type of bond.
336
- - MaturityDate: The maturity date of the bond.
337
- - IndicativeRate: The indicative rate of the bond.
338
- - Price: The price (PU) of the bond.
301
+ pd.DataFrame: A DataFrame containing the requested bond data. An empty
302
+ DataFrame is returned if no data is available for the specified date
303
+ (e.g., weekends, holidays, or future dates).
339
304
 
340
305
  Examples:
341
306
  >>> from pyield import anbima
@@ -347,30 +312,67 @@ def tpf_data(
347
312
  3 2025-08-22 LFT 2027-03-01 0.000302 17193.200289
348
313
  4 2025-08-22 LFT 2027-09-01 0.000411 17186.767105
349
314
  ...
315
+
316
+ DataFrame columns:
317
+ - BondType: Tipo do título público (e.g., 'LTN', 'NTN-B').
318
+ - ReferenceDate: Data de referência dos dados.
319
+ - SelicCode: Código do título no SELIC.
320
+ - IssueBaseDate: Data base ou de emissão do título.
321
+ - MaturityDate: Data de vencimento do título.
322
+ - BDToMat: Número de dias úteis entre a data de referência e o vencimento.
323
+ - Duration: Macaulay Duration do título em anos.
324
+ - DV01: Variação financeira no preço do título (em BRL) para uma
325
+ mudança de 1 basis point (0,01%) na taxa de juros.
326
+ - DV01USD: O mesmo que DV01, mas convertido para USD pela PTAX do dia.
327
+ - Price: Preço Unitário (PU) do título na data de referência.
328
+ - BidRate: Taxa de compra em formato decimal (e.g., 0.10 para 10%).
329
+ - AskRate: Taxa de venda em formato decimal.
330
+ - IndicativeRate: Taxa indicativa em formato decimal.
331
+ - DIRate: Taxa DI interpolada para o vencimento do título.
332
+ - StdDev: Desvio padrão da taxa indicativa.
333
+ - LowerBoundRateD0: Limite inferior do intervalo indicativo para D+0.
334
+ - UpperBoundRateD0: Limite superior do intervalo indicativo para D+0.
335
+ - LowerBoundRateD1: Limite inferior do intervalo indicativo para D+1.
336
+ - UpperBoundRateD1: Limite superior do intervalo indicativo para D+1.
337
+ - Criteria: Critério utilizado pela ANBIMA para o cálculo.
338
+
339
+ Notes:
340
+ - Data for the last 5 business days is available on the public ANBIMA
341
+ website.
342
+ - Historical data (older than 5 business days) is only available via
343
+ the RTM network. If you are not connected to RTM, requests for
344
+ older dates will return an empty DataFrame.
350
345
  """
351
- df = get_cached_dataset("tpf")
352
346
  date = convert_input_dates(date)
353
- df = df.query("ReferenceDate == @date").reset_index(drop=True)
354
347
 
355
- if df.empty:
348
+ date_log = date.strftime("%d/%m/%Y")
349
+ today = dt.datetime.now(BZ_TIMEZONE).date()
350
+ if date.date() > today:
351
+ logger.info(
352
+ f"Cannot fetch data for a future date ({date_log}). "
353
+ "Returning empty DataFrame."
354
+ )
355
+ return pd.DataFrame()
356
+
357
+ if fetch_from_source:
356
358
  # Try to fetch the data from the Anbima website
357
- df = fetch_tpf_data(date)
359
+ df = _fetch_tpf_data(date)
360
+ else:
361
+ df = (
362
+ get_cached_dataset("tpf")
363
+ .query("ReferenceDate == @date")
364
+ .reset_index(drop=True)
365
+ )
358
366
 
359
367
  if df.empty:
360
368
  # If the data is still empty, return an empty DataFrame
361
369
  return pd.DataFrame()
362
370
 
363
371
  if bond_type:
364
- df = df.query("BondType == @bond_type").reset_index(drop=True)
372
+ norm_bond_type = _bond_type_mapping(bond_type) # noqa
373
+ df = df.query("BondType == @norm_bond_type").reset_index(drop=True)
365
374
 
366
- if adj_maturities:
367
- df["MaturityDate"] = bday.offset(df["MaturityDate"], 0)
368
-
369
- return (
370
- df[["ReferenceDate", "BondType", "MaturityDate", "IndicativeRate", "Price"]]
371
- .sort_values(["BondType", "MaturityDate"])
372
- .reset_index(drop=True)
373
- )
375
+ return df.sort_values(["BondType", "MaturityDate"]).reset_index(drop=True)
374
376
 
375
377
 
376
378
  def tpf_fixed_rate_maturities(date: DateScalar) -> pd.Series:
@@ -1 +0,0 @@
1
- __version__ = "0.37.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
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
File without changes
File without changes
File without changes