PYield 0.35.6__tar.gz → 0.35.7__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 (52) hide show
  1. {pyield-0.35.6 → pyield-0.35.7}/PKG-INFO +1 -1
  2. pyield-0.35.7/pyield/__about__.py +1 -0
  3. {pyield-0.35.6 → pyield-0.35.7}/pyield/b3/di1.py +2 -2
  4. {pyield-0.35.6 → pyield-0.35.7}/pyield/bc/auction.py +4 -2
  5. {pyield-0.35.6 → pyield-0.35.7}/pyield/tn/auction.py +60 -21
  6. pyield-0.35.6/pyield/__about__.py +0 -1
  7. {pyield-0.35.6 → pyield-0.35.7}/.gitignore +0 -0
  8. {pyield-0.35.6 → pyield-0.35.7}/LICENSE +0 -0
  9. {pyield-0.35.6 → pyield-0.35.7}/README.md +0 -0
  10. {pyield-0.35.6 → pyield-0.35.7}/pyield/__init__.py +0 -0
  11. {pyield-0.35.6 → pyield-0.35.7}/pyield/anbima/__init__.py +0 -0
  12. {pyield-0.35.6 → pyield-0.35.7}/pyield/anbima/ettj.py +0 -0
  13. {pyield-0.35.6 → pyield-0.35.7}/pyield/anbima/ima.py +0 -0
  14. {pyield-0.35.6 → pyield-0.35.7}/pyield/anbima/imaq.py +0 -0
  15. {pyield-0.35.6 → pyield-0.35.7}/pyield/anbima/ipca.py +0 -0
  16. {pyield-0.35.6 → pyield-0.35.7}/pyield/anbima/tpf.py +0 -0
  17. {pyield-0.35.6 → pyield-0.35.7}/pyield/b3/__init__.py +0 -0
  18. {pyield-0.35.6 → pyield-0.35.7}/pyield/b3/di_over.py +0 -0
  19. {pyield-0.35.6 → pyield-0.35.7}/pyield/b3/futures/__init__.py +0 -0
  20. {pyield-0.35.6 → pyield-0.35.7}/pyield/b3/futures/common.py +0 -0
  21. {pyield-0.35.6 → pyield-0.35.7}/pyield/b3/futures/historical.py +0 -0
  22. {pyield-0.35.6 → pyield-0.35.7}/pyield/b3/futures/intraday.py +0 -0
  23. {pyield-0.35.6 → pyield-0.35.7}/pyield/b3/futures/xml.py +0 -0
  24. {pyield-0.35.6 → pyield-0.35.7}/pyield/bc/__init__.py +0 -0
  25. {pyield-0.35.6 → pyield-0.35.7}/pyield/bc/bcdata.py +0 -0
  26. {pyield-0.35.6 → pyield-0.35.7}/pyield/bc/ptax.py +0 -0
  27. {pyield-0.35.6 → pyield-0.35.7}/pyield/bc/repo.py +0 -0
  28. {pyield-0.35.6 → pyield-0.35.7}/pyield/bc/trades_intraday.py +0 -0
  29. {pyield-0.35.6 → pyield-0.35.7}/pyield/bc/trades_monthly.py +0 -0
  30. {pyield-0.35.6 → pyield-0.35.7}/pyield/bc/vna.py +0 -0
  31. {pyield-0.35.6 → pyield-0.35.7}/pyield/bday.py +0 -0
  32. {pyield-0.35.6 → pyield-0.35.7}/pyield/conftest.py +0 -0
  33. {pyield-0.35.6 → pyield-0.35.7}/pyield/data_cache.py +0 -0
  34. {pyield-0.35.6 → pyield-0.35.7}/pyield/date_converter.py +0 -0
  35. {pyield-0.35.6 → pyield-0.35.7}/pyield/fwd.py +0 -0
  36. {pyield-0.35.6 → pyield-0.35.7}/pyield/holidays/__init__.py +0 -0
  37. {pyield-0.35.6 → pyield-0.35.7}/pyield/holidays/br_holidays_new.txt +0 -0
  38. {pyield-0.35.6 → pyield-0.35.7}/pyield/holidays/br_holidays_old.txt +0 -0
  39. {pyield-0.35.6 → pyield-0.35.7}/pyield/holidays/brholidays.py +0 -0
  40. {pyield-0.35.6 → pyield-0.35.7}/pyield/ibge/__init__.py +0 -0
  41. {pyield-0.35.6 → pyield-0.35.7}/pyield/ibge/ipca.py +0 -0
  42. {pyield-0.35.6 → pyield-0.35.7}/pyield/interpolator.py +0 -0
  43. {pyield-0.35.6 → pyield-0.35.7}/pyield/py.typed +0 -0
  44. {pyield-0.35.6 → pyield-0.35.7}/pyield/retry.py +0 -0
  45. {pyield-0.35.6 → pyield-0.35.7}/pyield/tn/__init__.py +0 -0
  46. {pyield-0.35.6 → pyield-0.35.7}/pyield/tn/lft.py +0 -0
  47. {pyield-0.35.6 → pyield-0.35.7}/pyield/tn/ltn.py +0 -0
  48. {pyield-0.35.6 → pyield-0.35.7}/pyield/tn/ntnb.py +0 -0
  49. {pyield-0.35.6 → pyield-0.35.7}/pyield/tn/ntnc.py +0 -0
  50. {pyield-0.35.6 → pyield-0.35.7}/pyield/tn/ntnf.py +0 -0
  51. {pyield-0.35.6 → pyield-0.35.7}/pyield/tn/tools.py +0 -0
  52. {pyield-0.35.6 → pyield-0.35.7}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PYield
3
- Version: 0.35.6
3
+ Version: 0.35.7
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.35.7"
@@ -63,9 +63,9 @@ def data(
63
63
 
64
64
  df = get_cached_dataset("di1").query("TradeDate in @dates").reset_index(drop=True)
65
65
 
66
- today = dt.now(TIMEZONE_BZ)
66
+ today = dt.now(TIMEZONE_BZ).date()
67
67
  last_bday = bday.last_business_day()
68
- is_trading_day = today.date() == last_bday.date()
68
+ is_trading_day = today == last_bday.date()
69
69
  if is_trading_day and last_bday in df["TradeDate"]:
70
70
  logger.info("Trying to get DI Futures data for today.")
71
71
  df_intraday = b3.futures(contract_code="DI1", date=last_bday)
@@ -19,6 +19,8 @@ from pyield.retry import default_retry
19
19
  from pyield.tn.ntnb import duration as duration_b
20
20
  from pyield.tn.ntnf import duration as duration_f
21
21
 
22
+ logger = logging.getLogger(__name__)
23
+
22
24
  """
23
25
  Dicionário com o mapeamento das colunas da API do BC para o DataFrame final
24
26
  Chaves com comentário serão descartadas ao final do processamento
@@ -233,7 +235,7 @@ def _fetch_df_from_url(url: str) -> pd.DataFrame:
233
235
  try:
234
236
  df = _load_from_url(url)
235
237
  if df.empty:
236
- logging.warning("No auction data found for the specified period.")
238
+ logger.warning("No auction data found for the specified period.")
237
239
  return pd.DataFrame()
238
240
  df = _pre_process_df(df)
239
241
  df = _adjust_null_values(df)
@@ -244,7 +246,7 @@ def _fetch_df_from_url(url: str) -> pd.DataFrame:
244
246
  df = _sort_and_reorder_columns(df)
245
247
  return df
246
248
  except Exception:
247
- logging.exception("Error fetching auction data from BC API.")
249
+ logger.exception("Error fetching auction data from BC API.")
248
250
  return pd.DataFrame()
249
251
 
250
252
 
@@ -1,14 +1,14 @@
1
- """
2
- Manual da API: https://portal-conhecimento.tesouro.gov.br/catalogo-componentes/api-leil%C3%B5es
3
- Financeiro é sempre o financeiro aceito.
4
- Os dados do BCB só existem na API do TN.
5
- """
6
-
7
1
  import logging
2
+ from datetime import datetime as dt
3
+ from zoneinfo import ZoneInfo
8
4
 
9
5
  import pandas as pd
10
6
  import requests
11
7
 
8
+ logger = logging.getLogger(__name__)
9
+
10
+ TIMEZONE_BZ = ZoneInfo("America/Sao_Paulo")
11
+
12
12
  BENCHMARKS_COLUMN_MAPPING = {
13
13
  "BENCHMARK": "Benchmark",
14
14
  "VENCIMENTO": "MaturityDate",
@@ -18,7 +18,10 @@ BENCHMARKS_COLUMN_MAPPING = {
18
18
  }
19
19
 
20
20
 
21
- API_URL = "https://apiapex.tesouro.gov.br/aria/v1/api-leiloes-pub/custom/benchmarks?incluir_historico="
21
+ API_BASE_URL = (
22
+ "https://apiapex.tesouro.gov.br/aria/v1/api-leiloes-pub/custom/benchmarks"
23
+ )
24
+ API_HISTORY_PARAM = "incluir_historico"
22
25
 
23
26
 
24
27
  def benchmarks(include_history: bool = False) -> pd.DataFrame:
@@ -51,12 +54,12 @@ def benchmarks(include_history: bool = False) -> pd.DataFrame:
51
54
  * Rows with any `NaN` values are dropped before returning the DataFrame.
52
55
 
53
56
  Examples:
54
- >>> # Get current benchmarks
57
+ >>> # Get current benchmarks (default behavior)
55
58
  >>> from pyield import tn
56
59
  >>> df_current = tn.benchmarks()
57
60
 
58
61
  >>> # Get historical benchmarks
59
- >>> df_history = benchmarks(include_history=True)
62
+ >>> df_history = tn.benchmarks(include_history=True)
60
63
  >>> df_history.head()
61
64
  Benchmark MaturityDate BondType StartDate EndDate
62
65
  0 LFT 6 anos 2020-03-01 LFT 2014-01-01 2014-06-30
@@ -66,26 +69,62 @@ def benchmarks(include_history: bool = False) -> pd.DataFrame:
66
69
  4 LFT 1 ano 2022-03-01 LFT 2020-10-15 2021-03-31
67
70
  """
68
71
  session = requests.Session()
69
- include_history_param = "S" if include_history else "N"
70
- api_endpoint = API_URL + include_history_param
72
+ include_history_param_value = "S" if include_history else "N"
73
+ api_endpoint = f"{API_BASE_URL}?{API_HISTORY_PARAM}={include_history_param_value}"
74
+
71
75
  try:
72
76
  stn_benchmarks = session.get(api_endpoint)
77
+ stn_benchmarks.raise_for_status()
73
78
  except requests.exceptions.SSLError as e:
74
- logging.error(
79
+ logger.error(
75
80
  f"SSL error encountered: {e}. Retrying without certificate verification."
76
81
  )
77
82
  stn_benchmarks = session.get(api_endpoint, verify=False)
83
+ stn_benchmarks.raise_for_status()
84
+ except requests.exceptions.RequestException as e:
85
+ logger.error(f"Error fetching benchmarks from API: {e}")
86
+ return pd.DataFrame()
87
+
78
88
  response_dict = stn_benchmarks.json()
89
+
90
+ # 1a verificação: Verifica se a resposta da API contém dados válidos de registros
91
+ if not response_dict or not response_dict.get("registros"):
92
+ logger.warning("API response did not contain 'registros' key or it was empty.")
93
+ return pd.DataFrame()
94
+
95
+ # Tenta criar o DataFrame. O .dropna() pode resultar em um DF vazio.
79
96
  df = pd.DataFrame(response_dict["registros"]).dropna()
80
97
  df = df.convert_dtypes()
81
- # A API pode retornar vazio, então é necessário verificar
82
- if not df.empty:
83
- df["VENCIMENTO"] = pd.to_datetime(df["VENCIMENTO"])
84
- df["TERMINO"] = pd.to_datetime(df["TERMINO"])
85
- df["INÍCIO"] = pd.to_datetime(df["INÍCIO"])
86
- df["BENCHMARK"] = df["BENCHMARK"].str.strip()
87
- df["TÍTULO"] = df["TÍTULO"].str.strip()
88
- else:
89
- logging.warning("Não foi possível obter os benchmarks do STN.")
98
+
99
+ # 2a verificação: Verifica se o DataFrame resultante (pós-dropna) está vazio
100
+ # Esta verificação é importante porque .dropna() pode remover todas as linhas.
101
+ if df.empty:
102
+ logger.warning(
103
+ "No valid benchmark data found after initial processing"
104
+ "(e.g., all rows had NaNs and were dropped).",
105
+ )
106
+ return pd.DataFrame()
107
+
108
+ # Se chegamos até aqui, o DataFrame tem dados e pode ser processado.
109
+ df["VENCIMENTO"] = pd.to_datetime(df["VENCIMENTO"])
110
+ df["TERMINO"] = pd.to_datetime(df["TERMINO"])
111
+ df["INÍCIO"] = pd.to_datetime(df["INÍCIO"])
112
+ df["BENCHMARK"] = df["BENCHMARK"].str.strip()
113
+ df["TÍTULO"] = df["TÍTULO"].str.strip()
114
+
115
+ if not include_history:
116
+ # Em tese, a API já retorna apenas benchmarks ativos,
117
+ # mas vamos garantir que o DataFrame só contenha benchmarks ativos
118
+ # considerando o período atual.
119
+ today = dt.now(TIMEZONE_BZ).date()
120
+ today = pd.Timestamp(today)
121
+ df = df.query("INÍCIO <= @today <= TERMINO").reset_index(drop=True)
122
+
123
+ # Verifica novamente se o DataFrame ficou vazio *após o filtro condicional*
124
+ # (apenas se `include_history` for False)
125
+ if df.empty and not include_history:
126
+ logger.warning(
127
+ "No current benchmark data found after filtering by active period."
128
+ )
90
129
 
91
130
  return df.rename(columns=BENCHMARKS_COLUMN_MAPPING)
@@ -1 +0,0 @@
1
- __version__ = "0.35.6"
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