PYield 0.27.2__tar.gz → 0.27.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.
- {pyield-0.27.2 → pyield-0.27.3}/PKG-INFO +1 -1
- pyield-0.27.3/pyield/__about__.py +1 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/__init__.py +2 -2
- {pyield-0.27.2 → pyield-0.27.3}/pyield/anbima/ima.py +32 -28
- {pyield-0.27.2 → pyield-0.27.3}/pyield/indicators.py +23 -3
- pyield-0.27.2/pyield/__about__.py +0 -1
- {pyield-0.27.2 → pyield-0.27.3}/.gitignore +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/LICENSE +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/README.md +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/anbima/__init__.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/anbima/imaq.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/anbima/tpf.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/b3_futures/__init__.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/b3_futures/common.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/b3_futures/di.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/b3_futures/historical.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/b3_futures/intraday.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/b3_futures/xml.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/bc/auction.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/bc/repos.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/bday.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/conftest.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/data_cache.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/date_converter.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/holidays/__init__.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/holidays/br_holidays_new.txt +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/holidays/br_holidays_old.txt +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/holidays/brholidays.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/interpolator.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/projections.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/py.typed +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/tools.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/tpf/__init__.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/tpf/lft.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/tpf/ltn.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/tpf/ntnb.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/tpf/ntnf.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyield/tpf/tools.py +0 -0
- {pyield-0.27.2 → pyield-0.27.3}/pyproject.toml +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.27.3"
|
|
@@ -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+",
|
|
@@ -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
|
|
|
@@ -91,14 +94,14 @@ def _reorder_last_ima(df: pd.DataFrame) -> pd.DataFrame:
|
|
|
91
94
|
"SelicCode",
|
|
92
95
|
"ISIN",
|
|
93
96
|
"Weight",
|
|
94
|
-
"
|
|
95
|
-
"
|
|
97
|
+
"TheoreticalQuantity",
|
|
98
|
+
"PMR",
|
|
99
|
+
"Convexity",
|
|
96
100
|
"BDToMat",
|
|
97
101
|
"Duration",
|
|
102
|
+
"IndicativeRate",
|
|
103
|
+
"Price",
|
|
98
104
|
"DV01",
|
|
99
|
-
"PMR",
|
|
100
|
-
"Convexity",
|
|
101
|
-
"TheoreticalQuantity",
|
|
102
105
|
"MarketDV01",
|
|
103
106
|
"MarketQuantity",
|
|
104
107
|
"MarketValue",
|
|
@@ -106,7 +109,7 @@ def _reorder_last_ima(df: pd.DataFrame) -> pd.DataFrame:
|
|
|
106
109
|
return df[col_order].reset_index(drop=True)
|
|
107
110
|
|
|
108
111
|
|
|
109
|
-
def
|
|
112
|
+
def last_ima(ima_type: ima_types | None = None) -> pd.DataFrame:
|
|
110
113
|
"""
|
|
111
114
|
Fetch and process the last IMA market data available from ANBIMA.
|
|
112
115
|
|
|
@@ -121,23 +124,24 @@ def ima(ima_type: ima_types | None = None) -> pd.DataFrame:
|
|
|
121
124
|
|
|
122
125
|
Returns:
|
|
123
126
|
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
|
-
|
|
127
|
+
- Date: reference date of the data.
|
|
128
|
+
- IMAType: type of IMA index.
|
|
129
|
+
- BondType: type of bond.
|
|
130
|
+
- Maturity: bond maturity date.
|
|
131
|
+
- SelicCode: bond code in the SELIC system.
|
|
132
|
+
- ISIN: international Securities Identification Number.
|
|
133
|
+
- Weight: weight of the bond in the index.
|
|
134
|
+
- TheoreticalQuantity: theoretical quantity.
|
|
135
|
+
- PMR: average repurchase term.
|
|
136
|
+
- Convexity: convexity of the bond.
|
|
137
|
+
- BDToMat: business days to maturity.
|
|
138
|
+
- Duration: duration of the bond in years.
|
|
139
|
+
- IndicativeRate: indicative rate in percentage
|
|
140
|
+
- Price: bond price.
|
|
141
|
+
- DV01: DV01 in R$.
|
|
142
|
+
- MarketDV01: market DV01 in R$.
|
|
143
|
+
- MarketQuantity: market quantity.
|
|
144
|
+
- MarketValue: market value in R$.
|
|
141
145
|
|
|
142
146
|
Raises:
|
|
143
147
|
Exception: Logs error and returns an empty DataFrame if any error occurs during
|
|
@@ -153,5 +157,5 @@ def ima(ima_type: ima_types | None = None) -> pd.DataFrame:
|
|
|
153
157
|
df = df.sort_values(["IMAType", "BondType", "Maturity"]).reset_index(drop=True)
|
|
154
158
|
return df
|
|
155
159
|
except Exception as e:
|
|
156
|
-
|
|
160
|
+
logger.exception(f"Error fetching or processing the last IMA data: {e}")
|
|
157
161
|
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
|