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.
- {pyield-0.27.2 → pyield-0.27.4}/PKG-INFO +1 -1
- pyield-0.27.4/pyield/__about__.py +1 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/__init__.py +2 -2
- {pyield-0.27.2 → pyield-0.27.4}/pyield/anbima/ima.py +45 -34
- {pyield-0.27.2 → pyield-0.27.4}/pyield/indicators.py +23 -3
- pyield-0.27.2/pyield/__about__.py +0 -1
- {pyield-0.27.2 → pyield-0.27.4}/.gitignore +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/LICENSE +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/README.md +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/anbima/__init__.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/anbima/imaq.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/anbima/tpf.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/b3_futures/__init__.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/b3_futures/common.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/b3_futures/di.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/b3_futures/historical.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/b3_futures/intraday.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/b3_futures/xml.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/bc/auction.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/bc/repos.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/bday.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/conftest.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/data_cache.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/date_converter.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/holidays/__init__.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/holidays/br_holidays_new.txt +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/holidays/br_holidays_old.txt +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/holidays/brholidays.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/interpolator.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/projections.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/py.typed +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/tools.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/tpf/__init__.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/tpf/lft.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/tpf/ltn.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/tpf/ntnb.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/tpf/ntnf.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyield/tpf/tools.py +0 -0
- {pyield-0.27.2 → pyield-0.27.4}/pyproject.toml +0 -0
|
@@ -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
|
|
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
|
-
"
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"PMR": "PMR", #
|
|
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=
|
|
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 *
|
|
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["
|
|
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
|
|
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:
|
|
125
|
-
-
|
|
126
|
-
- BondType:
|
|
127
|
-
- Maturity:
|
|
128
|
-
- SelicCode:
|
|
129
|
-
- ISIN:
|
|
130
|
-
-
|
|
131
|
-
-
|
|
132
|
-
-
|
|
133
|
-
-
|
|
134
|
-
-
|
|
135
|
-
-
|
|
136
|
-
-
|
|
137
|
-
-
|
|
138
|
-
-
|
|
139
|
-
-
|
|
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
|
-
|
|
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}):
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|