PYield 0.27.2__tar.gz → 0.27.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.
Files changed (39) hide show
  1. {pyield-0.27.2 → pyield-0.27.4}/PKG-INFO +1 -1
  2. pyield-0.27.4/pyield/__about__.py +1 -0
  3. {pyield-0.27.2 → pyield-0.27.4}/pyield/__init__.py +2 -2
  4. {pyield-0.27.2 → pyield-0.27.4}/pyield/anbima/ima.py +45 -34
  5. {pyield-0.27.2 → pyield-0.27.4}/pyield/indicators.py +23 -3
  6. pyield-0.27.2/pyield/__about__.py +0 -1
  7. {pyield-0.27.2 → pyield-0.27.4}/.gitignore +0 -0
  8. {pyield-0.27.2 → pyield-0.27.4}/LICENSE +0 -0
  9. {pyield-0.27.2 → pyield-0.27.4}/README.md +0 -0
  10. {pyield-0.27.2 → pyield-0.27.4}/pyield/anbima/__init__.py +0 -0
  11. {pyield-0.27.2 → pyield-0.27.4}/pyield/anbima/imaq.py +0 -0
  12. {pyield-0.27.2 → pyield-0.27.4}/pyield/anbima/tpf.py +0 -0
  13. {pyield-0.27.2 → pyield-0.27.4}/pyield/b3_futures/__init__.py +0 -0
  14. {pyield-0.27.2 → pyield-0.27.4}/pyield/b3_futures/common.py +0 -0
  15. {pyield-0.27.2 → pyield-0.27.4}/pyield/b3_futures/di.py +0 -0
  16. {pyield-0.27.2 → pyield-0.27.4}/pyield/b3_futures/historical.py +0 -0
  17. {pyield-0.27.2 → pyield-0.27.4}/pyield/b3_futures/intraday.py +0 -0
  18. {pyield-0.27.2 → pyield-0.27.4}/pyield/b3_futures/xml.py +0 -0
  19. {pyield-0.27.2 → pyield-0.27.4}/pyield/bc/auction.py +0 -0
  20. {pyield-0.27.2 → pyield-0.27.4}/pyield/bc/repos.py +0 -0
  21. {pyield-0.27.2 → pyield-0.27.4}/pyield/bday.py +0 -0
  22. {pyield-0.27.2 → pyield-0.27.4}/pyield/conftest.py +0 -0
  23. {pyield-0.27.2 → pyield-0.27.4}/pyield/data_cache.py +0 -0
  24. {pyield-0.27.2 → pyield-0.27.4}/pyield/date_converter.py +0 -0
  25. {pyield-0.27.2 → pyield-0.27.4}/pyield/holidays/__init__.py +0 -0
  26. {pyield-0.27.2 → pyield-0.27.4}/pyield/holidays/br_holidays_new.txt +0 -0
  27. {pyield-0.27.2 → pyield-0.27.4}/pyield/holidays/br_holidays_old.txt +0 -0
  28. {pyield-0.27.2 → pyield-0.27.4}/pyield/holidays/brholidays.py +0 -0
  29. {pyield-0.27.2 → pyield-0.27.4}/pyield/interpolator.py +0 -0
  30. {pyield-0.27.2 → pyield-0.27.4}/pyield/projections.py +0 -0
  31. {pyield-0.27.2 → pyield-0.27.4}/pyield/py.typed +0 -0
  32. {pyield-0.27.2 → pyield-0.27.4}/pyield/tools.py +0 -0
  33. {pyield-0.27.2 → pyield-0.27.4}/pyield/tpf/__init__.py +0 -0
  34. {pyield-0.27.2 → pyield-0.27.4}/pyield/tpf/lft.py +0 -0
  35. {pyield-0.27.2 → pyield-0.27.4}/pyield/tpf/ltn.py +0 -0
  36. {pyield-0.27.2 → pyield-0.27.4}/pyield/tpf/ntnb.py +0 -0
  37. {pyield-0.27.2 → pyield-0.27.4}/pyield/tpf/ntnf.py +0 -0
  38. {pyield-0.27.2 → pyield-0.27.4}/pyield/tpf/tools.py +0 -0
  39. {pyield-0.27.2 → pyield-0.27.4}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PYield
3
- Version: 0.27.2
3
+ Version: 0.27.4
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.27.4"
@@ -2,7 +2,7 @@ import logging
2
2
 
3
3
  from pyield import bday
4
4
  from pyield.__about__ import __version__
5
- from pyield.anbima.ima import ima
5
+ from pyield.anbima.ima import last_ima
6
6
  from pyield.anbima.imaq import imaq
7
7
  from pyield.anbima.tpf import anbima_tpf_data, anbima_tpf_rates, tpf_pre_maturities
8
8
  from pyield.b3_futures import futures
@@ -26,7 +26,7 @@ __all__ = [
26
26
  "DIFutures",
27
27
  "forward_rates",
28
28
  "futures",
29
- "ima",
29
+ "last_ima",
30
30
  "imaq",
31
31
  "indicator",
32
32
  "Interpolator",
@@ -5,6 +5,8 @@ from typing import Literal
5
5
  import pandas as pd
6
6
  import requests
7
7
 
8
+ logger = logging.getLogger(__name__)
9
+
8
10
  ima_types = Literal[
9
11
  "IRF-M 1",
10
12
  "IRF-M 1+",
@@ -17,7 +19,7 @@ ima_types = Literal[
17
19
  "IMA-GERAL",
18
20
  ]
19
21
 
20
- LAST_IMA_COL_MAPPING = {
22
+ IMA_COL_MAPPING = {
21
23
  # "2",
22
24
  "Data de Referência": "Date",
23
25
  "INDICE": "IMAType",
@@ -27,17 +29,17 @@ LAST_IMA_COL_MAPPING = {
27
29
  "Código ISIN": "ISIN",
28
30
  "Taxa Indicativa (% a.a.)": "IndicativeRate",
29
31
  "PU (R$)": "Price",
30
- # "PU de Juros (R$)": "InterestPrice",
32
+ "PU de Juros (R$)": "InterestPrice",
31
33
  "Quantidade (1.000 títulos)": "MarketQuantity",
32
34
  "Quantidade Teórica (1.000 títulos)": "TheoreticalQuantity",
33
35
  "Carteira a Mercado (R$ mil)": "MarketValue",
34
36
  "Peso (%)": "Weight",
35
37
  "Prazo (d.u.)": "BDToMat",
36
38
  "Duration (d.u.)": "Duration",
37
- # "Número de Operações *": "NumberOfOperations",
38
- # "Quant. Negociada (1.000 títulos) *": "NegotiatedQuantity",
39
- # "Valor Negociado (R$ mil) *": "NegotiatedValue",
40
- "PMR": "PMR", # Prazmo médio de repactuação
39
+ "Número de Operações *": "NumberOfOperations",
40
+ "Quant. Negociada (1.000 títulos) *": "NegotiatedQuantity",
41
+ "Valor Negociado (R$ mil) *": "NegotiatedValue",
42
+ "PMR": "PMR", # Prazo médio de repactuação
41
43
  "Convexidade": "Convexity",
42
44
  }
43
45
 
@@ -64,7 +66,7 @@ def _fetch_last_ima() -> pd.DataFrame:
64
66
 
65
67
 
66
68
  def _process_last_ima(df: pd.DataFrame) -> pd.DataFrame:
67
- df = df.rename(columns=LAST_IMA_COL_MAPPING)[LAST_IMA_COL_MAPPING.values()]
69
+ df = df.rename(columns=IMA_COL_MAPPING)[IMA_COL_MAPPING.values()]
68
70
  df["IndicativeRate"] = (df["IndicativeRate"] / 100).round(6)
69
71
  df["Date"] = pd.to_datetime(df["Date"], format="%d/%m/%Y")
70
72
  df["Maturity"] = pd.to_datetime(df["Maturity"], format="%d/%m/%Y")
@@ -74,11 +76,12 @@ def _process_last_ima(df: pd.DataFrame) -> pd.DataFrame:
74
76
  # Duration is in business days, convert to years
75
77
  df["Duration"] /= 252
76
78
  mduration = df["Duration"] / (1 + df["IndicativeRate"])
77
- df["DV01"] = 0.0001 * df["Price"] * mduration * df["MarketQuantity"]
78
- # Since MarketDV01 is the total stock value, we round them to integer
79
- df["MarketDV01"] = df["DV01"].round(0).astype("Int64")
79
+ df["DV01"] = 0.0001 * mduration * df["Price"]
80
80
  # LFT DV01 is zero
81
- df["MarketDV01"] = df["MarketDV01"].where(df["BondType"] != "LFT", 0)
81
+ df["DV01"] = df["DV01"].where(df["BondType"] != "LFT", 0)
82
+ # Since MarketDV01 is the total stock value, we round them to integer
83
+ df["MarketDV01"] = (df["DV01"] * df["MarketQuantity"]).round(0).astype("Int64")
84
+
82
85
  return df
83
86
 
84
87
 
@@ -90,15 +93,19 @@ def _reorder_last_ima(df: pd.DataFrame) -> pd.DataFrame:
90
93
  "Maturity",
91
94
  "SelicCode",
92
95
  "ISIN",
93
- "Weight",
94
- "IndicativeRate",
95
- "Price",
96
96
  "BDToMat",
97
97
  "Duration",
98
+ "IndicativeRate",
99
+ "Price",
100
+ "InterestPrice",
98
101
  "DV01",
99
102
  "PMR",
103
+ "Weight",
100
104
  "Convexity",
101
105
  "TheoreticalQuantity",
106
+ "NumberOfOperations",
107
+ "NegotiatedQuantity",
108
+ "NegotiatedValue",
102
109
  "MarketDV01",
103
110
  "MarketQuantity",
104
111
  "MarketValue",
@@ -106,7 +113,7 @@ def _reorder_last_ima(df: pd.DataFrame) -> pd.DataFrame:
106
113
  return df[col_order].reset_index(drop=True)
107
114
 
108
115
 
109
- def ima(ima_type: ima_types | None = None) -> pd.DataFrame:
116
+ def last_ima(ima_type: ima_types | None = None) -> pd.DataFrame:
110
117
  """
111
118
  Fetch and process the last IMA market data available from ANBIMA.
112
119
 
@@ -121,28 +128,32 @@ def ima(ima_type: ima_types | None = None) -> pd.DataFrame:
121
128
 
122
129
  Returns:
123
130
  pd.DataFrame: A DataFrame containing the IMA data with the following columns:
124
- - Date: Reference date of the data.
125
- - Index: IMA index.
126
- - BondType: Type of bond.
127
- - Maturity: Bond maturity date.
128
- - SelicCode: Code representing the SELIC rate.
129
- - ISIN: International Securities Identification Number.
130
- - Price: Bond price.
131
- - Weight: Weight of the bond in the index.
132
- - BDToMat: Business days to maturity.
133
- - Duration: Duration of the bond in years.
134
- - PMR: Average repurchase term.
135
- - Convexity: Convexity of the bond.
136
- - TheoreticalQuantity: Theoretical quantity.
137
- - MarketDV01: Market DV01 in R$.
138
- - MarketQuantity: Market quantity.
139
- - MarketValue: Market value in R$.
140
-
131
+ - Date: reference date of the data.
132
+ - IMAType: type of IMA index.
133
+ - BondType: type of bond.
134
+ - Maturity: bond maturity date.
135
+ - SelicCode: bond code in the SELIC system.
136
+ - ISIN: international Securities Identification Number.
137
+ - BDToMat: business days to maturity.
138
+ - Duration: duration of the bond in business years (252 days/year).
139
+ - IndicativeRate: indicative rate.
140
+ - Price: bond price.
141
+ - InterestPrice: interest price.
142
+ - DV01: DV01 in R$.
143
+ - PMR: average repurchase term.
144
+ - Weight: weight of the bond in the index.
145
+ - Convexity: convexity of the bond.
146
+ - TheoreticalQuantity: theoretical quantity.
147
+ - NumberOfOperations: number of operations.
148
+ - NegotiatedQuantity: negotiated quantity.
149
+ - NegotiatedValue: negotiated value.
150
+ - MarketQuantity: market quantity.
151
+ - MarketDV01: market DV01 in R$.
152
+ - MarketValue: market value in R$.
141
153
 
142
154
  Raises:
143
155
  Exception: Logs error and returns an empty DataFrame if any error occurs during
144
156
  fetching or processing.
145
-
146
157
  """
147
158
  try:
148
159
  df = _fetch_last_ima()
@@ -153,5 +164,5 @@ def ima(ima_type: ima_types | None = None) -> pd.DataFrame:
153
164
  df = df.sort_values(["IMAType", "BondType", "Maturity"]).reset_index(drop=True)
154
165
  return df
155
166
  except Exception as e:
156
- logging.exception(f"Error fetching or processing the last IMA data: {e}")
167
+ logger.exception(f"Error fetching or processing the last IMA data: {e}")
157
168
  return pd.DataFrame()
@@ -263,14 +263,34 @@ def _fetch_with_retry(
263
263
  return process_response(data)
264
264
 
265
265
  except RequestException as e:
266
- msg = f"Erro ao buscar {error_message} (tentativa {attempt + 1}): {e}"
266
+ msg = f"Erro ao buscar {error_message} (tentativa {attempt + 1}): "
267
+
268
+ if hasattr(e, "response"):
269
+ response = e.response
270
+ if response is not None: # Verificação extra se response não é None
271
+ status_code = response.status_code
272
+ msg += f"Status Code: {status_code}, "
273
+ if status_code == requests.codes.not_found:
274
+ msg = f"Sem dado para {error_message} na data solicitada."
275
+ msg += f"API URL é: {api_url}"
276
+ logger.warning(msg)
277
+ return float("nan")
278
+ else:
279
+ status_code = "N/A (response is None ou response attribute não existe)"
280
+
281
+ msg += f"Exceção: {e}, Tipo da Exceção: {type(e)}"
282
+ msg += f" , Response Object Existe? {hasattr(e, 'response')}"
283
+
267
284
  logger.error(msg)
268
285
 
269
- except Exception as unhandled_error: # Outros erros - LÓGICA INESPERADA!
286
+ except Exception as unhandled_error:
270
287
  msg = f"Erro INESPERADO ao buscar {error_message}: {unhandled_error}"
271
288
  logger.exception(msg) # Loga stack trace completo para debug
289
+ break
272
290
 
273
291
  attempt += 1
274
292
  time.sleep(1.2**attempt) # Backoff exponencial
275
293
 
276
- raise ValueError(f"Falha ao buscar {error_message} após {MAX_ATTEMPTS} tentativas")
294
+ msg = f"Falha ao buscar {error_message} após {MAX_ATTEMPTS} tentativas."
295
+ msg += f" API URL: {api_url}"
296
+ raise ValueError(msg)
@@ -1 +0,0 @@
1
- __version__ = "0.27.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