PYield 0.26.4__tar.gz → 0.26.6__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.26.4 → pyield-0.26.6}/PKG-INFO +1 -1
- pyield-0.26.6/pyield/__about__.py +1 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/bc/auction.py +98 -90
- pyield-0.26.6/pyield/ima.py +154 -0
- pyield-0.26.4/pyield/__about__.py +0 -1
- pyield-0.26.4/pyield/ima.py +0 -143
- {pyield-0.26.4 → pyield-0.26.6}/.gitignore +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/LICENSE +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/README.md +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/__init__.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/anbima.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/b3_futures/__init__.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/b3_futures/common.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/b3_futures/di.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/b3_futures/historical.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/b3_futures/intraday.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/b3_futures/xml.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/bday.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/conftest.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/data_cache.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/date_converter.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/holidays/__init__.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/holidays/br_holidays_new.txt +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/holidays/br_holidays_old.txt +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/holidays/brholidays.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/indicators.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/interpolator.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/projections.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/py.typed +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/tools.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/tpf/__init__.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/tpf/lft.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/tpf/ltn.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/tpf/ntnb.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/tpf/ntnf.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyield/tpf/tools.py +0 -0
- {pyield-0.26.4 → pyield-0.26.6}/pyproject.toml +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.26.6"
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Documentação da API do BC
|
|
3
|
-
https://olinda.bcb.gov.br/olinda/servico/leiloes_selic/versao/v1/aplicacao#!/recursos/leiloesTitulosPublicos#eyJmb3JtdWxhcmlvIjp7IiRmb3JtYXQiOiJqc29uIiwiJHRvcCI6MTAwfX0=
|
|
3
|
+
https://olinda.bcb.gov.br/olinda/servico/leiloes_selic/versao/v1/aplicacao#!/recursos/leiloesTitulosPublicos#eyJmb3JtdWxhcmlvIjp7IiRmb3JtYXQiOiJqc29uIiwiJHRvcCI6MTAwfX0=
|
|
4
|
+
Exemplo de chamada:
|
|
5
|
+
https://olinda.bcb.gov.br/olinda/servico/leiloes_selic/versao/v1/odata/leiloesTitulosPublicos(dataMovimentoInicio=@dataMovimentoInicio,dataMovimentoFim=@dataMovimentoFim,dataLiquidacao=@dataLiquidacao,codigoTitulo=@codigoTitulo,dataVencimento=@dataVencimento,edital=@edital,tipoPublico=@tipoPublico,tipoOferta=@tipoOferta)?@dataMovimentoInicio='2025-01-30'&@dataMovimentoFim='2025-01-30'&@tipoOferta='Venda'&$top=100&$format=json
|
|
4
6
|
"""
|
|
5
7
|
|
|
6
8
|
import io
|
|
@@ -10,7 +12,7 @@ from typing import Literal
|
|
|
10
12
|
import pandas as pd
|
|
11
13
|
import requests
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
from pyield import bday
|
|
14
16
|
from pyield import date_converter as dc
|
|
15
17
|
from pyield.date_converter import DateScalar
|
|
16
18
|
from pyield.tpf.ntnb import duration as duration_b
|
|
@@ -128,7 +130,7 @@ def _adjust_null_values(df: pd.DataFrame) -> pd.DataFrame:
|
|
|
128
130
|
|
|
129
131
|
|
|
130
132
|
def _add_dv01(df: pd.DataFrame) -> pd.DataFrame:
|
|
131
|
-
df["BDToMat"] =
|
|
133
|
+
df["BDToMat"] = bday.count(start=df["Date"], end=df["Maturity"])
|
|
132
134
|
|
|
133
135
|
is_accepted = df["AcceptedQuantityFR"] != 0 # noqa
|
|
134
136
|
df_not_accepted = df.query("~@is_accepted").reset_index(drop=True)
|
|
@@ -170,16 +172,6 @@ def _add_dv01(df: pd.DataFrame) -> pd.DataFrame:
|
|
|
170
172
|
return df
|
|
171
173
|
|
|
172
174
|
|
|
173
|
-
def _filter_auction_by_type(
|
|
174
|
-
df: pd.DataFrame, auction_type: Literal["Sell", "Buy"]
|
|
175
|
-
) -> pd.DataFrame:
|
|
176
|
-
auction_type_mapping = {"Venda": "Sell", "Compra": "Buy"}
|
|
177
|
-
auction_type = auction_type_mapping.get(auction_type)
|
|
178
|
-
if auction_type:
|
|
179
|
-
df = df.query(f"AuctionType == '{auction_type}'").reset_index(drop=True)
|
|
180
|
-
return df
|
|
181
|
-
|
|
182
|
-
|
|
183
175
|
def _sort_and_reorder_columns(df: pd.DataFrame) -> pd.DataFrame:
|
|
184
176
|
column_sequence = [
|
|
185
177
|
"Date",
|
|
@@ -213,17 +205,16 @@ def _sort_and_reorder_columns(df: pd.DataFrame) -> pd.DataFrame:
|
|
|
213
205
|
return df[column_sequence].sort_values(by=primary_sort_keys).reset_index(drop=True)
|
|
214
206
|
|
|
215
207
|
|
|
216
|
-
def _fetch_df_from_url(
|
|
217
|
-
url: str,
|
|
218
|
-
auction_type: Literal["Sell", "Buy"] = None,
|
|
219
|
-
) -> pd.DataFrame:
|
|
208
|
+
def _fetch_df_from_url(url: str) -> pd.DataFrame:
|
|
220
209
|
try:
|
|
221
210
|
df = _load_from_url(url)
|
|
211
|
+
if df.empty:
|
|
212
|
+
logging.warning("No auction data found for the specified period.")
|
|
213
|
+
return pd.DataFrame()
|
|
222
214
|
df = _pre_process_df(df)
|
|
223
215
|
df = _adjust_null_values(df)
|
|
224
216
|
df = _process_df(df)
|
|
225
217
|
df = _add_dv01(df)
|
|
226
|
-
df = _filter_auction_by_type(df, auction_type)
|
|
227
218
|
df = _sort_and_reorder_columns(df)
|
|
228
219
|
return df
|
|
229
220
|
except Exception:
|
|
@@ -231,92 +222,109 @@ def _fetch_df_from_url(
|
|
|
231
222
|
return pd.DataFrame()
|
|
232
223
|
|
|
233
224
|
|
|
234
|
-
def get_all_auctions() -> pd.DataFrame:
|
|
235
|
-
"""
|
|
236
|
-
Retrieves a DataFrame containing all public auction data from the BC.
|
|
237
|
-
|
|
238
|
-
This function fetches the complete series of public auctions conducted by the
|
|
239
|
-
National Treasury via the BC API and processes it to a standardized DataFrame.
|
|
240
|
-
|
|
241
|
-
Returns:
|
|
242
|
-
pd.DataFrame: A DataFrame sorted by Date, AuctionType, BondType, and Maturity
|
|
243
|
-
containing auction data. In case of an error during data retrieval, an empty
|
|
244
|
-
DataFrame is returned and an error message is logged.
|
|
245
|
-
"""
|
|
246
|
-
# URL com toda a série de leilões de títulos públicos do Tesouro Nacional
|
|
247
|
-
url = f"{BASE_API_URL}$format=text/csv"
|
|
248
|
-
|
|
249
|
-
return _fetch_df_from_url(url)
|
|
250
|
-
|
|
251
|
-
|
|
252
225
|
def auctions(
|
|
253
|
-
start: DateScalar,
|
|
226
|
+
start: DateScalar | None = None,
|
|
254
227
|
end: DateScalar | None = None,
|
|
255
228
|
auction_type: Literal["Sell", "Buy"] = None,
|
|
256
229
|
) -> pd.DataFrame:
|
|
257
230
|
"""
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
231
|
+
Recupera dados de leilões para um determinado período e tipo de leilão da API do BC.
|
|
232
|
+
|
|
233
|
+
**Consultas de Período:**
|
|
234
|
+
- Para consultar dados de um intervalo, forneça as datas de `start` e `end`.
|
|
235
|
+
Exemplo: `auctions(start='2024-10-20', end='2024-10-27')`
|
|
236
|
+
- Se apenas `start` for fornecido, a API do BC retornará dados de leilão a partir
|
|
237
|
+
da data de `start` **até a data mais recente disponível**.
|
|
238
|
+
Exemplo: `auctions(start='2024-10-20')`
|
|
239
|
+
- Se apenas `end` for fornecido, a API do BC retornará dados de leilão **desde a
|
|
240
|
+
data mais antiga disponível até a data de `end`**.
|
|
241
|
+
Exemplo: `auctions(end='2024-10-27')`
|
|
242
|
+
|
|
243
|
+
**Série Histórica Completa:**
|
|
244
|
+
- Para recuperar a série histórica completa de leilões (desde 12/11/2012 até o
|
|
245
|
+
último dia útil), chame a função sem fornecer os parâmetros `start` e `end`.
|
|
246
|
+
Exemplo: `auctions()`
|
|
247
|
+
|
|
248
|
+
Busca dados de leilões da API do BC para as datas de início e fim especificadas,
|
|
249
|
+
filtrando os resultados diretamente na API pelo tipo de leilão, se especificado.
|
|
250
|
+
O comportamento da função em relação aos parâmetros `start` e `end` segue o padrão
|
|
251
|
+
da API do Banco Central:
|
|
252
|
+
- Se `start` for fornecido e `end` não, a função retorna dados de `start` até o fim.
|
|
253
|
+
- Se `end` for fornecido e `start` não, a API retorna dados do início até `end`.
|
|
254
|
+
- Se ambos `start` e `end` forem omitidos, a API retorna a série histórica completa.
|
|
255
|
+
|
|
256
|
+
Os dados podem ser filtrados pelo tipo de leilão especificado ("Sell" ou "Buy").
|
|
257
|
+
Leilões de "Sell" são aqueles em que o Tesouro Nacional vende títulos ao mercado.
|
|
258
|
+
Leilões de "Buy" são aqueles em que o Tesouro Nacional compra títulos do mercado.
|
|
265
259
|
|
|
266
260
|
Args:
|
|
267
|
-
start (DateScalar):
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
261
|
+
start (DateScalar, opcional): A data de início para a consulta dos leilões.
|
|
262
|
+
Se `start` for fornecido e `end` for `None`, a API retornará dados de
|
|
263
|
+
leilão a partir de `start` até a data mais recente disponível.
|
|
264
|
+
Se `start` e `end` forem `None`, a série histórica completa será retornada.
|
|
265
|
+
Padrão é `None`.
|
|
266
|
+
end (DateScalar, opcional): A data de fim para a consulta de dados de leilão.
|
|
267
|
+
Se `end` for fornecido e `start` for `None`, a API retornará dados de
|
|
268
|
+
leilão desde a data mais antiga disponível até a data de `end`.
|
|
269
|
+
Se `start` e `end` forem `None`, a série histórica completa será retornada.
|
|
270
|
+
Padrão é `None`.
|
|
271
|
+
auction_type (Literal["Sell", "Buy"], opcional): O tipo de leilão para filtrar
|
|
272
|
+
diretamente na API. Padrão é `None` (retorna todos os tipos de leilão).
|
|
272
273
|
|
|
273
274
|
Returns:
|
|
274
|
-
pd.DataFrame:
|
|
275
|
-
|
|
276
|
-
|
|
275
|
+
pd.DataFrame: Um DataFrame contendo dados de leilões para o período e tipo
|
|
276
|
+
especificados. Em caso de erro ao buscar os dados, um DataFrame vazio
|
|
277
|
+
é retornado e uma mensagem de erro é registrada no log.
|
|
277
278
|
|
|
278
279
|
Notes:
|
|
279
|
-
FR = First Round
|
|
280
|
-
SR = Second Round
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
- Date:
|
|
284
|
-
- Settlement:
|
|
285
|
-
- AuctionType:
|
|
286
|
-
- Ordinance:
|
|
287
|
-
- Buyer:
|
|
288
|
-
- BondType:
|
|
289
|
-
- Maturity:
|
|
290
|
-
- SelicCode:
|
|
291
|
-
- Value:
|
|
292
|
-
- ValueFR:
|
|
293
|
-
- ValueSR:
|
|
294
|
-
- OfferedQuantity:
|
|
295
|
-
- OfferedQuantityFR:
|
|
296
|
-
- OfferedQuantitySR:
|
|
297
|
-
- AcceptedQuantity:
|
|
298
|
-
- AcceptedQuantityFR:
|
|
299
|
-
- AcceptedQuantitySR:
|
|
300
|
-
- AvgPrice:
|
|
301
|
-
- CutPrice:
|
|
302
|
-
- AvgRate:
|
|
303
|
-
- CutRate:
|
|
304
|
-
- BDToMat:
|
|
305
|
-
- Duration: Duration
|
|
306
|
-
- MDuration: Modified
|
|
307
|
-
- DV01FR:
|
|
308
|
-
|
|
280
|
+
FR = First Round (Primeira Rodada)
|
|
281
|
+
SR = Second Round (Segunda Rodada)
|
|
282
|
+
|
|
283
|
+
O DataFrame possui as seguintes colunas:
|
|
284
|
+
- Date: Data do leilão.
|
|
285
|
+
- Settlement: Data de liquidação do leilão.
|
|
286
|
+
- AuctionType: Tipo de leilão (ex: "Sell" ou "Buy").
|
|
287
|
+
- Ordinance: Edital normativo associado ao leilão.
|
|
288
|
+
- Buyer: Categoria do comprador (ex: "TodoMercado", "SomenteDealerApto").
|
|
289
|
+
- BondType: Categoria do título (ex: "LTN", "LFT", "NTN-B", "NTN-F").
|
|
290
|
+
- Maturity: Data de vencimento do título.
|
|
291
|
+
- SelicCode: Código do título no sistema Selic.
|
|
292
|
+
- Value: Valor total do leilão em R$ (FR + SR).
|
|
293
|
+
- ValueFR: Valor da primeira rodada (FR) do leilão em R$.
|
|
294
|
+
- ValueSR: Valor da segunda rodada (SR) em R$.
|
|
295
|
+
- OfferedQuantity: Quantidade total ofertada no leilão (FR + SR).
|
|
296
|
+
- OfferedQuantityFR: Quantidade ofertada na primeira rodada (FR).
|
|
297
|
+
- OfferedQuantitySR: Quantidade ofertada na segunda rodada (SR).
|
|
298
|
+
- AcceptedQuantity: Quantidade total aceita no leilão (FR + SR).
|
|
299
|
+
- AcceptedQuantityFR: Quantidade aceita na primeira rodada (FR).
|
|
300
|
+
- AcceptedQuantitySR: Quantidade aceita na segunda rodada (SR).
|
|
301
|
+
- AvgPrice: Preço médio no leilão.
|
|
302
|
+
- CutPrice: Preço de corte.
|
|
303
|
+
- AvgRate: Taxa de juros média.
|
|
304
|
+
- CutRate: Taxa de corte.
|
|
305
|
+
- BDToMat: Dias úteis até o vencimento.
|
|
306
|
+
- Duration: Duration (Duração) do título.
|
|
307
|
+
- MDuration: Modified Duration (Duração Modificada) do título.
|
|
308
|
+
- DV01FR: Dollar Value of 1 bp change in yield - Primeira Rodada (FR) em R$.
|
|
309
309
|
"""
|
|
310
|
+
url = BASE_API_URL
|
|
311
|
+
if start:
|
|
312
|
+
start = dc.convert_input_dates(start)
|
|
313
|
+
start_str = start.strftime("%Y-%m-%d")
|
|
314
|
+
url += f"@dataMovimentoInicio='{start_str}'"
|
|
310
315
|
|
|
311
|
-
start = dc.convert_input_dates(start)
|
|
312
316
|
if end:
|
|
313
317
|
end = dc.convert_input_dates(end)
|
|
314
|
-
|
|
315
|
-
|
|
318
|
+
end_str = end.strftime("%Y-%m-%d")
|
|
319
|
+
url += f"&@dataMovimentoFim='{end_str}'"
|
|
316
320
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
+
# Mapeamento do auction_type para o valor esperado pela API
|
|
322
|
+
auction_type_mapping = {"Sell": "Venda", "Buy": "Compra"}
|
|
323
|
+
auction_type_api_value = auction_type_mapping.get(auction_type)
|
|
324
|
+
# Adiciona o parâmetro tipoOferta à URL se auction_type for fornecido
|
|
325
|
+
if auction_type_api_value:
|
|
326
|
+
url += f"&@tipoOferta='{auction_type_api_value}'"
|
|
321
327
|
|
|
322
|
-
|
|
328
|
+
url += "&$format=text/csv" # Adiciona o formato CSV ao final
|
|
329
|
+
|
|
330
|
+
return _fetch_df_from_url(url)
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
ima_types = Literal[
|
|
9
|
+
"IRF-M 1",
|
|
10
|
+
"IRF-M 1+",
|
|
11
|
+
"IRF-M",
|
|
12
|
+
"IMA-B 5",
|
|
13
|
+
"IMA-B 5+",
|
|
14
|
+
"IMA-B",
|
|
15
|
+
"IMA-S",
|
|
16
|
+
"IMA-GERAL-EX-C",
|
|
17
|
+
"IMA-GERAL",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
LAST_IMA_COL_MAPPING = {
|
|
21
|
+
# "2",
|
|
22
|
+
"Data de Referência": "Date",
|
|
23
|
+
"INDICE": "IMAType",
|
|
24
|
+
"Títulos": "BondType",
|
|
25
|
+
"Data de Vencimento": "Maturity",
|
|
26
|
+
"Código SELIC": "SelicCode",
|
|
27
|
+
"Código ISIN": "ISIN",
|
|
28
|
+
"Taxa Indicativa (% a.a.)": "IndicativeRate",
|
|
29
|
+
"PU (R$)": "Price",
|
|
30
|
+
# "PU de Juros (R$)": "InterestPrice",
|
|
31
|
+
"Quantidade (1.000 títulos)": "MarketQuantity",
|
|
32
|
+
"Quantidade Teórica (1.000 títulos)": "TheoreticalQuantity",
|
|
33
|
+
"Carteira a Mercado (R$ mil)": "MarketValue",
|
|
34
|
+
"Peso (%)": "Weight",
|
|
35
|
+
"Prazo (d.u.)": "BDToMat",
|
|
36
|
+
"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
|
|
41
|
+
"Convexidade": "Convexity",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
LAST_IMA_URL = "https://www.anbima.com.br/informacoes/ima/arqs/ima_completo.txt"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _fetch_last_ima() -> pd.DataFrame:
|
|
49
|
+
r = requests.get(LAST_IMA_URL)
|
|
50
|
+
r.raise_for_status()
|
|
51
|
+
r.encoding = "latin1"
|
|
52
|
+
text = r.text.split("2@COMPOSIÇÃO DE CARTEIRA")[1].strip()
|
|
53
|
+
string_io_buffer = io.StringIO(text)
|
|
54
|
+
|
|
55
|
+
df = pd.read_csv(
|
|
56
|
+
string_io_buffer,
|
|
57
|
+
sep="@",
|
|
58
|
+
decimal=",",
|
|
59
|
+
thousands=".",
|
|
60
|
+
dtype_backend="numpy_nullable",
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return df
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _process_last_ima(df: pd.DataFrame) -> pd.DataFrame:
|
|
67
|
+
df = df.rename(columns=LAST_IMA_COL_MAPPING)[LAST_IMA_COL_MAPPING.values()]
|
|
68
|
+
df["IndicativeRate"] = (df["IndicativeRate"] / 100).round(6)
|
|
69
|
+
df["Date"] = pd.to_datetime(df["Date"], format="%d/%m/%Y")
|
|
70
|
+
df["Maturity"] = pd.to_datetime(df["Maturity"], format="%d/%m/%Y")
|
|
71
|
+
df["MarketQuantity"] = (1000 * df["MarketQuantity"]).astype("Int64")
|
|
72
|
+
df["Price"] = df["Price"].round(6)
|
|
73
|
+
# Duration is in business days, convert to years
|
|
74
|
+
df["Duration"] /= 252
|
|
75
|
+
mduration = df["Duration"] / (1 + df["IndicativeRate"])
|
|
76
|
+
dv01 = 0.0001 * df["Price"] * mduration * df["MarketQuantity"]
|
|
77
|
+
# Since DV01 and MarketValue are total stock values, we round them to integers
|
|
78
|
+
df["MarketDV01"] = dv01.round(0).astype("Int64")
|
|
79
|
+
df["MarketValue"] = (1000 * df["MarketValue"]).round(0).astype("Int64")
|
|
80
|
+
return df
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _reorder_last_ima(df: pd.DataFrame) -> pd.DataFrame:
|
|
84
|
+
col_order = [
|
|
85
|
+
"Date",
|
|
86
|
+
"IMAType",
|
|
87
|
+
"BondType",
|
|
88
|
+
"Maturity",
|
|
89
|
+
"SelicCode",
|
|
90
|
+
"ISIN",
|
|
91
|
+
"Weight",
|
|
92
|
+
"IndicativeRate",
|
|
93
|
+
"Price",
|
|
94
|
+
"BDToMat",
|
|
95
|
+
"Duration",
|
|
96
|
+
"PMR",
|
|
97
|
+
"Convexity",
|
|
98
|
+
"TheoreticalQuantity",
|
|
99
|
+
"MarketDV01",
|
|
100
|
+
"MarketQuantity",
|
|
101
|
+
"MarketValue",
|
|
102
|
+
]
|
|
103
|
+
return df[col_order].reset_index(drop=True)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def ima(ima_type: ima_types | None = None) -> pd.DataFrame:
|
|
107
|
+
"""
|
|
108
|
+
Fetch and process the last IMA market data available from ANBIMA.
|
|
109
|
+
|
|
110
|
+
This function processes the data into a structured DataFrame.
|
|
111
|
+
It handles conversion of date formats, renames columns to English, and converts
|
|
112
|
+
certain numeric columns to integer types. In the event of an error during data
|
|
113
|
+
fetching or processing, an empty DataFrame is returned.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
ima_type (str, optional): Type of IMA index to filter the data. If None, all
|
|
117
|
+
IMA indexes are returned. Defaults to None.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
pd.DataFrame: A DataFrame containing the IMA data with the following columns:
|
|
121
|
+
- Date: Reference date of the data.
|
|
122
|
+
- Index: IMA index.
|
|
123
|
+
- BondType: Type of bond.
|
|
124
|
+
- Maturity: Bond maturity date.
|
|
125
|
+
- SelicCode: Code representing the SELIC rate.
|
|
126
|
+
- ISIN: International Securities Identification Number.
|
|
127
|
+
- Price: Bond price.
|
|
128
|
+
- Weight: Weight of the bond in the index.
|
|
129
|
+
- BDToMat: Business days to maturity.
|
|
130
|
+
- Duration: Duration of the bond in years.
|
|
131
|
+
- PMR: Average repurchase term.
|
|
132
|
+
- Convexity: Convexity of the bond.
|
|
133
|
+
- TheoreticalQuantity: Theoretical quantity.
|
|
134
|
+
- MarketDV01: Market DV01 in R$.
|
|
135
|
+
- MarketQuantity: Market quantity.
|
|
136
|
+
- MarketValue: Market value in R$.
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
Raises:
|
|
140
|
+
Exception: Logs error and returns an empty DataFrame if any error occurs during
|
|
141
|
+
fetching or processing.
|
|
142
|
+
|
|
143
|
+
"""
|
|
144
|
+
try:
|
|
145
|
+
df = _fetch_last_ima()
|
|
146
|
+
df = _process_last_ima(df)
|
|
147
|
+
df = _reorder_last_ima(df)
|
|
148
|
+
if ima_type is not None:
|
|
149
|
+
df = df.query("IMAType == @ima_type").reset_index(drop=True)
|
|
150
|
+
df = df.sort_values(["IMAType", "BondType", "Maturity"]).reset_index(drop=True)
|
|
151
|
+
return df
|
|
152
|
+
except Exception as e:
|
|
153
|
+
logging.exception(f"Error fetching or processing the last IMA data: {e}")
|
|
154
|
+
return pd.DataFrame()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.26.4"
|
pyield-0.26.4/pyield/ima.py
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import io
|
|
2
|
-
import logging
|
|
3
|
-
import re
|
|
4
|
-
import warnings
|
|
5
|
-
|
|
6
|
-
import pandas as pd
|
|
7
|
-
import requests
|
|
8
|
-
from bs4 import XMLParsedAsHTMLWarning
|
|
9
|
-
|
|
10
|
-
from pyield import date_converter as dc
|
|
11
|
-
from pyield.date_converter import DateScalar
|
|
12
|
-
|
|
13
|
-
IMA_URL = "https://www.anbima.com.br/informacoes/ima/ima-quantidade-mercado.asp"
|
|
14
|
-
COLUMN_MAPPING = {
|
|
15
|
-
"Data Referência": "Date",
|
|
16
|
-
"Título": "BondType",
|
|
17
|
-
"Data de Vencimento": "Maturity",
|
|
18
|
-
"Codigo Selic": "SelicCode",
|
|
19
|
-
"Código ISIN": "ISIN",
|
|
20
|
-
"PU (R$)": "Price",
|
|
21
|
-
"Quantidade em Mercado (1.000 Títulos)": "MarketQuantity",
|
|
22
|
-
"Valor de Mercado (R$ Mil)": "MarketValue",
|
|
23
|
-
"Variação da Quantidade (1.000 Títulos)": "QuantityVariation",
|
|
24
|
-
"Status do Titulo": "BondStatus",
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def _fetch_url_tables(target_date: pd.Timestamp) -> pd.DataFrame:
|
|
29
|
-
# Warning supression for BeautifulSoup, as the text is valid HTML and not XML
|
|
30
|
-
warnings.filterwarnings("ignore", category=XMLParsedAsHTMLWarning)
|
|
31
|
-
|
|
32
|
-
target_date_str = target_date.strftime("%d/%m/%Y")
|
|
33
|
-
payload = {
|
|
34
|
-
"Tipo": "",
|
|
35
|
-
"DataRef": "",
|
|
36
|
-
"Pai": "ima",
|
|
37
|
-
"Dt_Ref_Ver": "20250117",
|
|
38
|
-
"Dt_Ref": f"{target_date_str}",
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
r = requests.post(IMA_URL, data=payload, timeout=10)
|
|
42
|
-
r.raise_for_status()
|
|
43
|
-
r.encoding = "iso-8859-1"
|
|
44
|
-
if "Não há dados disponíveis" in r.text:
|
|
45
|
-
logging.warning("No data found. Returning empty DataFrame.")
|
|
46
|
-
return pd.DataFrame()
|
|
47
|
-
|
|
48
|
-
string_io_buffer = io.StringIO(r.text)
|
|
49
|
-
|
|
50
|
-
dfs = pd.read_html(
|
|
51
|
-
string_io_buffer,
|
|
52
|
-
flavor="html5lib",
|
|
53
|
-
attrs={"width": "100%"},
|
|
54
|
-
header=0,
|
|
55
|
-
thousands=".",
|
|
56
|
-
decimal=",",
|
|
57
|
-
displayed_only=True,
|
|
58
|
-
dtype_backend="numpy_nullable",
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
df = (
|
|
62
|
-
pd.concat(dfs)
|
|
63
|
-
.query("`Data de Vencimento`.notnull()")
|
|
64
|
-
.query("Título!='Título'")
|
|
65
|
-
.drop_duplicates(subset="Código ISIN")
|
|
66
|
-
.reset_index(drop=True)
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
# Convert to CSV and then back to pandas to get automatic type conversion
|
|
70
|
-
csv_buffer = io.StringIO(df.to_csv(index=False))
|
|
71
|
-
df = pd.read_csv(csv_buffer, dtype_backend="numpy_nullable")
|
|
72
|
-
|
|
73
|
-
# Expressão regular para datas no formato dd/mm/ano
|
|
74
|
-
date_pattern = r"\b(\d{2}/\d{2}/\d{4})\b"
|
|
75
|
-
|
|
76
|
-
# Busca pela primeira data
|
|
77
|
-
match = re.search(date_pattern, r.text)
|
|
78
|
-
|
|
79
|
-
if match:
|
|
80
|
-
anbima_date_str = match.group(1)
|
|
81
|
-
anbima_date = pd.to_datetime(anbima_date_str, format="%d/%m/%Y")
|
|
82
|
-
else:
|
|
83
|
-
logging.warning("No date found. Returning empty DataFrame.")
|
|
84
|
-
return pd.DataFrame()
|
|
85
|
-
|
|
86
|
-
df["Data Referência"] = anbima_date
|
|
87
|
-
|
|
88
|
-
return df
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
def _process_df(df: pd.DataFrame) -> pd.DataFrame:
|
|
92
|
-
df = df.rename(columns=COLUMN_MAPPING)[COLUMN_MAPPING.values()]
|
|
93
|
-
df["Maturity"] = pd.to_datetime(df["Maturity"], format="%d/%m/%Y")
|
|
94
|
-
# Converter a valores para inteiros
|
|
95
|
-
df["MarketQuantity"] = (1000 * df["MarketQuantity"]).astype("Int64")
|
|
96
|
-
df["MarketValue"] = (1000 * df["MarketValue"]).astype("Int64")
|
|
97
|
-
df["QuantityVariation"] = (1000 * df["QuantityVariation"]).astype("Int64")
|
|
98
|
-
|
|
99
|
-
return df
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def ima(date: DateScalar) -> pd.DataFrame:
|
|
103
|
-
"""
|
|
104
|
-
Fetch and process IMA market data for a given date.
|
|
105
|
-
|
|
106
|
-
This function retrieves IMA market data for the specified date from ANBIMA,
|
|
107
|
-
processes the data into a structured DataFrame, and returns the resulting DataFrame.
|
|
108
|
-
It handles conversion of date formats, renames columns to English, and converts
|
|
109
|
-
certain numeric columns to integer types. In the event of an error during data
|
|
110
|
-
fetching or processing, an empty DataFrame is returned.
|
|
111
|
-
|
|
112
|
-
Args:
|
|
113
|
-
date (DateScalar): A date-like object representing the target date for fetching
|
|
114
|
-
the data.
|
|
115
|
-
|
|
116
|
-
Returns:
|
|
117
|
-
pd.DataFrame: A DataFrame containing the IMA data with the following columns:
|
|
118
|
-
- Date: Reference date of the data.
|
|
119
|
-
- BondType: Type of bond.
|
|
120
|
-
- Maturity: Bond maturity date.
|
|
121
|
-
- SelicCode: Code representing the SELIC rate.
|
|
122
|
-
- ISIN: International Securities Identification Number.
|
|
123
|
-
- Price: Bond price.
|
|
124
|
-
- MarketQuantity: Market quantity (in units of 1000 bonds).
|
|
125
|
-
- MarketValue: Market value (in units of 1000 reais).
|
|
126
|
-
- QuantityVariation: Variation in quantity (in units of 1000 bonds).
|
|
127
|
-
- BondStatus: Status of the bond.
|
|
128
|
-
|
|
129
|
-
Raises:
|
|
130
|
-
Exception: Logs error and returns an empty DataFrame if any error occurs during
|
|
131
|
-
fetching or processing.
|
|
132
|
-
|
|
133
|
-
"""
|
|
134
|
-
try:
|
|
135
|
-
date = dc.convert_input_dates(date)
|
|
136
|
-
df = _fetch_url_tables(date)
|
|
137
|
-
df = _process_df(df)
|
|
138
|
-
return df
|
|
139
|
-
except Exception as e:
|
|
140
|
-
date_str = date.strftime("%d/%m/%Y")
|
|
141
|
-
logging.error(f"Error fetching IMA for {date_str}. Returning empty DataFrame.")
|
|
142
|
-
logging.error(f"Error message: {e}", exc_info=True)
|
|
143
|
-
return pd.DataFrame()
|
|
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
|