PYield 0.44.0__tar.gz → 0.44.2__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.44.0 → pyield-0.44.2}/.gitignore +3 -1
- {pyield-0.44.0 → pyield-0.44.2}/PKG-INFO +2 -1
- pyield-0.44.2/pyield/__about__.py +1 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/anbima/difusao.py +27 -26
- {pyield-0.44.0 → pyield-0.44.2}/pyield/anbima/ettj_intraday.py +2 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/anbima/ettj_last.py +2 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/anbima/ima.py +27 -25
- {pyield-0.44.0 → pyield-0.44.2}/pyield/anbima/imaq.py +18 -16
- {pyield-0.44.0 → pyield-0.44.2}/pyield/anbima/tpf.py +27 -43
- {pyield-0.44.0 → pyield-0.44.2}/pyield/b3/futures/__init__.py +24 -34
- {pyield-0.44.0 → pyield-0.44.2}/pyield/b3/futures/historical.py +1 -2
- {pyield-0.44.0 → pyield-0.44.2}/pyield/b3/intraday_derivatives.py +2 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/b3/price_report.py +32 -19
- {pyield-0.44.0 → pyield-0.44.2}/pyield/bc/auction.py +26 -26
- {pyield-0.44.0 → pyield-0.44.2}/pyield/bc/ptax_api.py +10 -8
- {pyield-0.44.0 → pyield-0.44.2}/pyield/bc/rates.py +2 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/bc/repo.py +18 -18
- {pyield-0.44.0 → pyield-0.44.2}/pyield/bc/trades_intraday.py +37 -37
- {pyield-0.44.0 → pyield-0.44.2}/pyield/bc/trades_monthly.py +24 -24
- {pyield-0.44.0 → pyield-0.44.2}/pyield/bc/vna.py +2 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/ipca/projected.py +2 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/selic/__init__.py +3 -3
- {pyield-0.44.0 → pyield-0.44.2}/pyield/selic/cpm.py +122 -3
- pyield-0.44.2/pyield/selic/probabilities.py +340 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/tn/auctions.py +26 -26
- {pyield-0.44.0 → pyield-0.44.2}/pyield/tn/benchmark.py +10 -10
- {pyield-0.44.0 → pyield-0.44.2}/pyproject.toml +1 -0
- pyield-0.44.0/pyield/__about__.py +0 -1
- {pyield-0.44.0 → pyield-0.44.2}/LICENSE +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/README.md +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/__init__.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/_internal/__init__.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/_internal/converters.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/_internal/data_cache.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/_internal/retry.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/_internal/types.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/anbima/__init__.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/b3/__init__.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/b3/_contracts.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/b3/_validar_pregao.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/b3/di1.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/b3/di_over.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/b3/futures/common.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/b3/futures/intraday.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/bc/__init__.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/bc/copom.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/bday/__init__.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/bday/core.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/bday/holidays/br_holidays_new.txt +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/bday/holidays/br_holidays_old.txt +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/bday/holidays/brholidays.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/clock.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/fwd.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/interpolator.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/ipca/__init__.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/ipca/historical.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/py.typed +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/rmd.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/tn/__init__.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/tn/lft.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/tn/ltn.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/tn/ntnb.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/tn/ntnb1.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/tn/ntnbprinc.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/tn/ntnc.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/tn/ntnf.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/tn/pre.py +0 -0
- {pyield-0.44.0 → pyield-0.44.2}/pyield/tn/tools.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyield
|
|
3
|
-
Version: 0.44.
|
|
3
|
+
Version: 0.44.2
|
|
4
4
|
Summary: A Python library for analysis of fixed income instruments in Brazil
|
|
5
5
|
Project-URL: Homepage, https://github.com/crdcj/PYield
|
|
6
6
|
Project-URL: Documentation, https://crdcj.github.io/PYield
|
|
@@ -36,6 +36,7 @@ Classifier: Operating System :: OS Independent
|
|
|
36
36
|
Classifier: Programming Language :: Python :: 3
|
|
37
37
|
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
38
38
|
Requires-Python: >=3.12
|
|
39
|
+
Requires-Dist: cachetools>=5.0.0
|
|
39
40
|
Requires-Dist: fastexcel>=0.19.0
|
|
40
41
|
Requires-Dist: lxml>=6.0.0
|
|
41
42
|
Requires-Dist: polars>=1.33.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.44.2"
|
|
@@ -18,31 +18,31 @@ URL_PAGINA_INICIAL = f"{URL_BASE}/taxasOnline.asp"
|
|
|
18
18
|
URL_CONSULTA_DADOS = f"{URL_BASE}/exibedados.asp"
|
|
19
19
|
URL_DOWNLOAD = f"{URL_BASE}/download_dados.asp?extensao=csv"
|
|
20
20
|
|
|
21
|
-
MAPA_COLUNAS =
|
|
22
|
-
"Título"
|
|
23
|
-
"Vencimento"
|
|
24
|
-
"Código ISIN"
|
|
25
|
-
"Provedor"
|
|
26
|
-
"Edital"
|
|
27
|
-
"Horário"
|
|
28
|
-
"Prazo"
|
|
29
|
-
"Lote"
|
|
30
|
-
"Fech D-1"
|
|
31
|
-
"Indicativo Superior"
|
|
32
|
-
"Máxima"
|
|
33
|
-
"Média"
|
|
34
|
-
"Mínima"
|
|
35
|
-
"Indicativo Inferior"
|
|
36
|
-
"Última"
|
|
37
|
-
"Oferta Compra"
|
|
38
|
-
"Oferta Venda"
|
|
39
|
-
"Nº de Negócios"
|
|
40
|
-
"Quantidade Negociada"
|
|
41
|
-
"Volume Negociado (R$)"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
ESQUEMA_API = {
|
|
45
|
-
ALIAS_COLUNAS = {
|
|
21
|
+
MAPA_COLUNAS = [
|
|
22
|
+
("Título", "titulo", pl.String),
|
|
23
|
+
("Vencimento", "data_vencimento", pl.String),
|
|
24
|
+
("Código ISIN", "codigo_isin", pl.String),
|
|
25
|
+
("Provedor", "provedor", pl.String),
|
|
26
|
+
("Edital", "edital", pl.String),
|
|
27
|
+
("Horário", "horario", pl.String),
|
|
28
|
+
("Prazo", "prazo", pl.Int64),
|
|
29
|
+
("Lote", "lote", pl.String),
|
|
30
|
+
("Fech D-1", "taxa_indicativa_anterior", pl.Float64),
|
|
31
|
+
("Indicativo Superior", "taxa_limite_superior", pl.Float64),
|
|
32
|
+
("Máxima", "taxa_maxima", pl.Float64),
|
|
33
|
+
("Média", "taxa_media", pl.Float64),
|
|
34
|
+
("Mínima", "taxa_minima", pl.Float64),
|
|
35
|
+
("Indicativo Inferior", "taxa_limite_inferior", pl.Float64),
|
|
36
|
+
("Última", "taxa_ultima", pl.Float64),
|
|
37
|
+
("Oferta Compra", "taxa_compra", pl.Float64),
|
|
38
|
+
("Oferta Venda", "taxa_venda", pl.Float64),
|
|
39
|
+
("Nº de Negócios", "num_negocios", pl.Int64),
|
|
40
|
+
("Quantidade Negociada", "quantidade_negociada", pl.Int64),
|
|
41
|
+
("Volume Negociado (R$)", "volume_negociado", pl.Float64),
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
ESQUEMA_API = {csv: tipo for csv, _, tipo in MAPA_COLUNAS}
|
|
45
|
+
ALIAS_COLUNAS = {csv: novo for csv, novo, _ in MAPA_COLUNAS}
|
|
46
46
|
|
|
47
47
|
# Colunas não selecionadas estão vazias na API.
|
|
48
48
|
ORDEM_COLUNAS_FINAL = [
|
|
@@ -211,9 +211,10 @@ def tpf_difusao(data_referencia: DateLike) -> pl.DataFrame:
|
|
|
211
211
|
- taxa_ultima (float): Última taxa negociada (decimal).
|
|
212
212
|
"""
|
|
213
213
|
if any_is_empty(data_referencia):
|
|
214
|
-
logger.warning("Nenhuma data fornecida. Retornando DataFrame vazio.")
|
|
215
214
|
return pl.DataFrame()
|
|
216
215
|
data = cv.converter_datas(data_referencia)
|
|
216
|
+
if not bday.is_business_day(data):
|
|
217
|
+
return pl.DataFrame()
|
|
217
218
|
data_str = data.strftime("%d/%m/%Y")
|
|
218
219
|
csv_data = _buscar_dados_url(data_str)
|
|
219
220
|
|
|
@@ -2,6 +2,7 @@ import datetime as dt
|
|
|
2
2
|
import logging
|
|
3
3
|
from io import StringIO
|
|
4
4
|
|
|
5
|
+
import cachetools
|
|
5
6
|
import polars as pl
|
|
6
7
|
import requests
|
|
7
8
|
|
|
@@ -18,6 +19,7 @@ URL_ETTJ_INTRADAY = (
|
|
|
18
19
|
CASAS_DECIMAIS = 6
|
|
19
20
|
|
|
20
21
|
|
|
22
|
+
@cachetools.cached(cache=cachetools.TTLCache(maxsize=16, ttl=60))
|
|
21
23
|
@retry_padrao
|
|
22
24
|
def _buscar_texto_intraday() -> str:
|
|
23
25
|
carga_requisicao = {"Dt_Ref": "", "saida": "csv"}
|
|
@@ -2,6 +2,7 @@ import datetime as dt
|
|
|
2
2
|
import logging
|
|
3
3
|
from io import StringIO
|
|
4
4
|
|
|
5
|
+
import cachetools
|
|
5
6
|
import polars as pl
|
|
6
7
|
import requests
|
|
7
8
|
|
|
@@ -15,6 +16,7 @@ URL_ULTIMA_ETTJ = "https://www.anbima.com.br/informacoes/est-termo/CZ-down.asp"
|
|
|
15
16
|
CASAS_DECIMAIS = 6
|
|
16
17
|
|
|
17
18
|
|
|
19
|
+
@cachetools.cached(cache=cachetools.TTLCache(maxsize=16, ttl=60))
|
|
18
20
|
@retry_padrao
|
|
19
21
|
def _buscar_texto_ultima_ettj() -> str:
|
|
20
22
|
"""Busca o texto bruto da curva de juros na ANBIMA."""
|
|
@@ -2,6 +2,7 @@ import io
|
|
|
2
2
|
import logging
|
|
3
3
|
from typing import Literal
|
|
4
4
|
|
|
5
|
+
import cachetools
|
|
5
6
|
import polars as pl
|
|
6
7
|
import requests
|
|
7
8
|
|
|
@@ -24,33 +25,33 @@ TiposIMA = Literal[
|
|
|
24
25
|
|
|
25
26
|
# Única fonte de verdade para colunas do CSV: (novo_nome, tipo)
|
|
26
27
|
# Colunas com None no nome são descartadas após a leitura.
|
|
27
|
-
IMA_COLUNAS =
|
|
28
|
-
"2"
|
|
29
|
-
"Data de Referência"
|
|
30
|
-
"INDICE"
|
|
31
|
-
"Títulos"
|
|
32
|
-
"Data de Vencimento"
|
|
33
|
-
"Código SELIC"
|
|
34
|
-
"Código ISIN"
|
|
35
|
-
"Taxa Indicativa (% a.a.)"
|
|
36
|
-
"PU (R$)"
|
|
37
|
-
"PU de Juros (R$)"
|
|
38
|
-
"Quantidade (1.000 títulos)"
|
|
39
|
-
"Quantidade Teórica (1.000 títulos)"
|
|
40
|
-
"Carteira a Mercado (R$ mil)"
|
|
41
|
-
"Peso (%)"
|
|
42
|
-
"Prazo (d.u.)"
|
|
43
|
-
"Duration (d.u.)"
|
|
44
|
-
"Número de Operações *"
|
|
45
|
-
"Quant. Negociada (1.000 títulos) *"
|
|
46
|
-
"Valor Negociado (R$ mil) *"
|
|
47
|
-
"PMR"
|
|
48
|
-
"Convexidade"
|
|
49
|
-
|
|
28
|
+
IMA_COLUNAS = [
|
|
29
|
+
("2", None, pl.Int64),
|
|
30
|
+
("Data de Referência", "Date", pl.String),
|
|
31
|
+
("INDICE", "IMAType", pl.String),
|
|
32
|
+
("Títulos", "BondType", pl.String),
|
|
33
|
+
("Data de Vencimento", "Maturity", pl.String),
|
|
34
|
+
("Código SELIC", "SelicCode", pl.Int64),
|
|
35
|
+
("Código ISIN", "ISIN", pl.String),
|
|
36
|
+
("Taxa Indicativa (% a.a.)", "IndicativeRate", pl.Float64),
|
|
37
|
+
("PU (R$)", "Price", pl.Float64),
|
|
38
|
+
("PU de Juros (R$)", "InterestPrice", pl.Float64),
|
|
39
|
+
("Quantidade (1.000 títulos)", "MarketQuantity", pl.Float64),
|
|
40
|
+
("Quantidade Teórica (1.000 títulos)", "TheoreticalQuantity", pl.Float64),
|
|
41
|
+
("Carteira a Mercado (R$ mil)", "MarketValue", pl.Float64),
|
|
42
|
+
("Peso (%)", "Weight", pl.Float64),
|
|
43
|
+
("Prazo (d.u.)", "BDToMat", pl.Int64),
|
|
44
|
+
("Duration (d.u.)", "Duration", pl.Int64),
|
|
45
|
+
("Número de Operações *", "NumberOfOperations", pl.Int64),
|
|
46
|
+
("Quant. Negociada (1.000 títulos) *", "NegotiatedQuantity", pl.Float64),
|
|
47
|
+
("Valor Negociado (R$ mil) *", "NegotiatedValue", pl.Float64),
|
|
48
|
+
("PMR", "PMR", pl.Float64),
|
|
49
|
+
("Convexidade", "Convexity", pl.Float64),
|
|
50
|
+
]
|
|
50
51
|
|
|
51
52
|
# Derivados automaticamente
|
|
52
|
-
ESQUEMA_IMA = {
|
|
53
|
-
MAPA_COLUNAS_IMA = {
|
|
53
|
+
ESQUEMA_IMA = {csv: tipo for csv, _, tipo in IMA_COLUNAS}
|
|
54
|
+
MAPA_COLUNAS_IMA = {csv: novo for csv, novo, _ in IMA_COLUNAS if novo is not None}
|
|
54
55
|
|
|
55
56
|
ORDEM_COLUNAS_FINAL = [
|
|
56
57
|
"Date",
|
|
@@ -81,6 +82,7 @@ ORDEM_COLUNAS_FINAL = [
|
|
|
81
82
|
URL_ULTIMO_IMA = "https://www.anbima.com.br/informacoes/ima/arqs/ima_completo.txt"
|
|
82
83
|
|
|
83
84
|
|
|
85
|
+
@cachetools.cached(cache=cachetools.TTLCache(maxsize=16, ttl=60))
|
|
84
86
|
@retry_padrao
|
|
85
87
|
def _buscar_texto_ultimo_ima() -> str:
|
|
86
88
|
resposta = requests.get(URL_ULTIMO_IMA, timeout=3)
|
|
@@ -17,27 +17,28 @@ from lxml.html import HTMLParser
|
|
|
17
17
|
from lxml.html import fromstring as html_fromstring
|
|
18
18
|
|
|
19
19
|
import pyield._internal.converters as cv
|
|
20
|
-
from pyield
|
|
20
|
+
from pyield import bday
|
|
21
21
|
from pyield._internal.types import DateLike, any_is_empty
|
|
22
|
+
from pyield.anbima.tpf import tpf_data
|
|
22
23
|
|
|
23
24
|
logger = logging.getLogger(__name__)
|
|
24
25
|
|
|
25
26
|
URL_IMA = "https://www.anbima.com.br/informacoes/ima/ima-quantidade-mercado.asp"
|
|
26
27
|
|
|
27
|
-
MAPA_COLUNAS =
|
|
28
|
-
"Título"
|
|
29
|
-
"Codigo Selic"
|
|
30
|
-
"Código ISIN"
|
|
31
|
-
"Data de Vencimento"
|
|
32
|
-
"Quantidade em Mercado (1.000 Títulos)"
|
|
33
|
-
"PU (R$)"
|
|
34
|
-
"Valor de Mercado (R$ Mil)"
|
|
35
|
-
"Variação da Quantidade (1.000 Títulos)"
|
|
36
|
-
"Status do Titulo"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
ALIAS_COLUNAS = {
|
|
40
|
-
ESQUEMA_DADOS = {
|
|
28
|
+
MAPA_COLUNAS = [
|
|
29
|
+
("Título", "BondType", pl.String),
|
|
30
|
+
("Codigo Selic", "SelicCode", pl.Int64),
|
|
31
|
+
("Código ISIN", "ISIN", pl.String),
|
|
32
|
+
("Data de Vencimento", "MaturityDate", pl.String),
|
|
33
|
+
("Quantidade em Mercado (1.000 Títulos)", "MarketQuantity", pl.Float64),
|
|
34
|
+
("PU (R$)", "Price", pl.Float64),
|
|
35
|
+
("Valor de Mercado (R$ Mil)", "MarketValue", pl.Float64),
|
|
36
|
+
("Variação da Quantidade (1.000 Títulos)", "QuantityVariation", pl.Float64),
|
|
37
|
+
("Status do Titulo", "BondStatus", pl.String),
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
ALIAS_COLUNAS = {html: novo for html, novo, _ in MAPA_COLUNAS}
|
|
41
|
+
ESQUEMA_DADOS = {novo: tipo for _, novo, tipo in MAPA_COLUNAS}
|
|
41
42
|
|
|
42
43
|
COLUNAS_INT = [
|
|
43
44
|
"MarketQuantity",
|
|
@@ -246,9 +247,10 @@ def imaq(date: DateLike) -> pl.DataFrame:
|
|
|
246
247
|
True
|
|
247
248
|
"""
|
|
248
249
|
if any_is_empty(date):
|
|
249
|
-
logger.warning("Nenhuma data informada. Retornando DataFrame vazio.")
|
|
250
250
|
return pl.DataFrame()
|
|
251
251
|
data = cv.converter_datas(date)
|
|
252
|
+
if not bday.is_business_day(data):
|
|
253
|
+
return pl.DataFrame()
|
|
252
254
|
data_str = data.strftime("%d/%m/%Y")
|
|
253
255
|
try:
|
|
254
256
|
url_content = _buscar_conteudo_url(data)
|
|
@@ -9,15 +9,15 @@ import requests
|
|
|
9
9
|
from requests.exceptions import HTTPError, RequestException
|
|
10
10
|
|
|
11
11
|
from pyield import bday, clock
|
|
12
|
-
from pyield.b3 import di1
|
|
13
|
-
from pyield.bc.ptax_api import ptax
|
|
14
12
|
from pyield._internal.converters import converter_datas
|
|
15
13
|
from pyield._internal.data_cache import obter_dataset_cacheado
|
|
16
14
|
from pyield._internal.retry import retry_padrao
|
|
15
|
+
from pyield._internal.types import DateLike, any_is_empty
|
|
16
|
+
from pyield.b3 import di1
|
|
17
|
+
from pyield.bc.ptax_api import ptax
|
|
17
18
|
from pyield.tn.ntnb import duration as duration_b
|
|
18
19
|
from pyield.tn.ntnc import duration as duration_c
|
|
19
20
|
from pyield.tn.ntnf import duration as duration_f
|
|
20
|
-
from pyield._internal.types import DateLike, any_is_empty
|
|
21
21
|
|
|
22
22
|
BOND_TYPES = Literal["LFT", "NTN-B", "NTN-C", "LTN", "NTN-F", "PRE"]
|
|
23
23
|
|
|
@@ -31,29 +31,29 @@ DATA_MUDANCA_FORMATO = dt.date(2014, 5, 13)
|
|
|
31
31
|
|
|
32
32
|
DIAS_RETENCAO_PUBLICA = 5
|
|
33
33
|
|
|
34
|
-
# Única fonte de verdade para colunas do CSV: (
|
|
34
|
+
# Única fonte de verdade para colunas do CSV: (nome_csv, nome_novo, tipo)
|
|
35
35
|
# Colunas de data são lidas como String e convertidas em _processar_df_bruto
|
|
36
|
-
TPF_COLUNAS =
|
|
37
|
-
"Titulo"
|
|
38
|
-
"Data Referencia"
|
|
39
|
-
"Codigo SELIC"
|
|
40
|
-
"Data Base/Emissao"
|
|
41
|
-
"Data Vencimento"
|
|
42
|
-
"Tx. Compra"
|
|
43
|
-
"Tx. Venda"
|
|
44
|
-
"Tx. Indicativas"
|
|
45
|
-
"PU"
|
|
46
|
-
"Desvio padrao"
|
|
47
|
-
"Interv. Ind. Inf. (D0)"
|
|
48
|
-
"Interv. Ind. Sup. (D0)"
|
|
49
|
-
"Interv. Ind. Inf. (D+1)"
|
|
50
|
-
"Interv. Ind. Sup. (D+1)"
|
|
51
|
-
"Criterio"
|
|
52
|
-
|
|
36
|
+
TPF_COLUNAS = [
|
|
37
|
+
("Titulo", "BondType", pl.String),
|
|
38
|
+
("Data Referencia", "ReferenceDate", pl.String),
|
|
39
|
+
("Codigo SELIC", "SelicCode", pl.Int64),
|
|
40
|
+
("Data Base/Emissao", "IssueBaseDate", pl.String),
|
|
41
|
+
("Data Vencimento", "MaturityDate", pl.String),
|
|
42
|
+
("Tx. Compra", "BidRate", pl.Float64),
|
|
43
|
+
("Tx. Venda", "AskRate", pl.Float64),
|
|
44
|
+
("Tx. Indicativas", "IndicativeRate", pl.Float64),
|
|
45
|
+
("PU", "Price", pl.Float64),
|
|
46
|
+
("Desvio padrao", "StdDev", pl.Float64),
|
|
47
|
+
("Interv. Ind. Inf. (D0)", "LowerBoundRateD0", pl.Float64),
|
|
48
|
+
("Interv. Ind. Sup. (D0)", "UpperBoundRateD0", pl.Float64),
|
|
49
|
+
("Interv. Ind. Inf. (D+1)", "LowerBoundRateD1", pl.Float64),
|
|
50
|
+
("Interv. Ind. Sup. (D+1)", "UpperBoundRateD1", pl.Float64),
|
|
51
|
+
("Criterio", "Criteria", pl.String),
|
|
52
|
+
]
|
|
53
53
|
|
|
54
54
|
# Derivados automaticamente
|
|
55
|
-
ESQUEMA_TPF = {
|
|
56
|
-
MAPA_NOMES_COLUNAS = {
|
|
55
|
+
ESQUEMA_TPF = {csv: tipo for csv, _, tipo in TPF_COLUNAS}
|
|
56
|
+
MAPA_NOMES_COLUNAS = {csv: novo for csv, novo, _ in TPF_COLUNAS}
|
|
57
57
|
|
|
58
58
|
logger = logging.getLogger(__name__)
|
|
59
59
|
|
|
@@ -345,25 +345,7 @@ def tpf_data(
|
|
|
345
345
|
|
|
346
346
|
Examples:
|
|
347
347
|
>>> from pyield import anbima
|
|
348
|
-
>>> anbima.tpf_data(date="
|
|
349
|
-
shape: (49, 14)
|
|
350
|
-
┌───────────────┬──────────┬───────────┬───────────────┬───┬───────────┬───────────┬────────────────┬──────────┐
|
|
351
|
-
│ ReferenceDate ┆ BondType ┆ SelicCode ┆ IssueBaseDate ┆ … ┆ BidRate ┆ AskRate ┆ IndicativeRate ┆ DIRate │
|
|
352
|
-
│ --- ┆ --- ┆ --- ┆ --- ┆ ┆ --- ┆ --- ┆ --- ┆ --- │
|
|
353
|
-
│ date ┆ str ┆ i64 ┆ date ┆ ┆ f64 ┆ f64 ┆ f64 ┆ f64 │
|
|
354
|
-
╞═══════════════╪══════════╪═══════════╪═══════════════╪═══╪═══════════╪═══════════╪════════════════╪══════════╡
|
|
355
|
-
│ 2025-08-22 ┆ LFT ┆ 210100 ┆ 2000-07-01 ┆ … ┆ 0.000198 ┆ 0.0001 ┆ 0.000165 ┆ 0.14906 │
|
|
356
|
-
│ 2025-08-22 ┆ LFT ┆ 210100 ┆ 2000-07-01 ┆ … ┆ -0.000053 ┆ -0.000156 ┆ -0.000116 ┆ 0.14843 │
|
|
357
|
-
│ 2025-08-22 ┆ LFT ┆ 210100 ┆ 2000-07-01 ┆ … ┆ -0.000053 ┆ -0.000143 ┆ -0.000107 ┆ 0.1436 │
|
|
358
|
-
│ 2025-08-22 ┆ LFT ┆ 210100 ┆ 2000-07-01 ┆ … ┆ 0.000309 ┆ 0.000292 ┆ 0.000302 ┆ 0.138189 │
|
|
359
|
-
│ 2025-08-22 ┆ LFT ┆ 210100 ┆ 2000-07-01 ┆ … ┆ 0.000421 ┆ 0.000399 ┆ 0.000411 ┆ 0.134548 │
|
|
360
|
-
│ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … ┆ … │
|
|
361
|
-
│ 2025-08-22 ┆ NTN-F ┆ 950199 ┆ 2016-01-15 ┆ … ┆ 0.139379 ┆ 0.139163 ┆ 0.139268 ┆ 0.13959 │
|
|
362
|
-
│ 2025-08-22 ┆ NTN-F ┆ 950199 ┆ 2018-01-05 ┆ … ┆ 0.134252 ┆ 0.134018 ┆ 0.13414 ┆ 0.1327 │
|
|
363
|
-
│ 2025-08-22 ┆ NTN-F ┆ 950199 ┆ 2020-01-10 ┆ … ┆ 0.13846 ┆ 0.138355 ┆ 0.13841 ┆ 0.13626 │
|
|
364
|
-
│ 2025-08-22 ┆ NTN-F ┆ 950199 ┆ 2022-01-07 ┆ … ┆ 0.139503 ┆ 0.139321 ┆ 0.139398 ┆ 0.13807 │
|
|
365
|
-
│ 2025-08-22 ┆ NTN-F ┆ 950199 ┆ 2024-01-05 ┆ … ┆ 0.140673 ┆ 0.140566 ┆ 0.140633 ┆ 0.13845 │
|
|
366
|
-
└───────────────┴──────────┴───────────┴───────────────┴───┴───────────┴───────────┴────────────────┴──────────┘
|
|
348
|
+
>>> df = anbima.tpf_data(date="06-02-2026")
|
|
367
349
|
|
|
368
350
|
Data columns:
|
|
369
351
|
- BondType: Tipo do título público (e.g., 'LTN', 'NTN-B').
|
|
@@ -399,13 +381,15 @@ def tpf_data(
|
|
|
399
381
|
datas com mais de 5 dias úteis. O acesso ao histórico completo
|
|
400
382
|
requer uma conexão à rede RTM. Sem ela, a consulta para datas
|
|
401
383
|
antigas retornará um DataFrame vazio.
|
|
402
|
-
"""
|
|
384
|
+
"""
|
|
403
385
|
if any_is_empty(date):
|
|
404
386
|
return pl.DataFrame()
|
|
405
387
|
date = converter_datas(date)
|
|
406
388
|
_validar_data_nao_futura(date)
|
|
407
389
|
|
|
408
390
|
if fetch_from_source:
|
|
391
|
+
if not bday.is_business_day(date):
|
|
392
|
+
return pl.DataFrame()
|
|
409
393
|
# Tenta buscar os dados diretamente da fonte (ANBIMA)
|
|
410
394
|
df = _buscar_dados_tpf(date)
|
|
411
395
|
else:
|
|
@@ -1,28 +1,15 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from collections.abc import Sequence
|
|
3
|
-
from typing import Literal
|
|
4
2
|
|
|
5
3
|
import polars as pl
|
|
6
4
|
|
|
7
5
|
import pyield._internal.converters as cv
|
|
8
6
|
from pyield import clock
|
|
7
|
+
from pyield._internal.retry import DadoIndisponivelError
|
|
9
8
|
from pyield._internal.types import DateLike, any_is_empty
|
|
10
9
|
from pyield.b3._contracts import normalizar_codigos_contrato
|
|
11
10
|
from pyield.b3._validar_pregao import data_negociacao_valida
|
|
12
11
|
from pyield.b3.futures import historical, intraday
|
|
13
12
|
|
|
14
|
-
OpcoesContrato = Literal[
|
|
15
|
-
"DI1",
|
|
16
|
-
"DDI",
|
|
17
|
-
"FRC",
|
|
18
|
-
"FRO",
|
|
19
|
-
"DAP",
|
|
20
|
-
"DOL",
|
|
21
|
-
"WDO",
|
|
22
|
-
"IND",
|
|
23
|
-
"WIN",
|
|
24
|
-
]
|
|
25
|
-
|
|
26
13
|
logger = logging.getLogger(__name__)
|
|
27
14
|
|
|
28
15
|
|
|
@@ -36,12 +23,17 @@ def _buscar_intraday_ou_historico(
|
|
|
36
23
|
return pl.DataFrame()
|
|
37
24
|
|
|
38
25
|
if horario_atual >= intraday.HORA_FIM_INTRADAY:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
26
|
+
for tentativa_full_report in (full_report, not full_report):
|
|
27
|
+
try:
|
|
28
|
+
df_historico = historical.historical(
|
|
29
|
+
data_negociacao, codigos_contrato, tentativa_full_report
|
|
30
|
+
)
|
|
31
|
+
except DadoIndisponivelError:
|
|
32
|
+
continue
|
|
33
|
+
if not df_historico.is_empty():
|
|
34
|
+
logger.info("Dados consolidados disponíveis. Usando histórico.")
|
|
35
|
+
return df_historico
|
|
36
|
+
logger.info("Histórico consolidado indisponível. Tentando intraday.")
|
|
45
37
|
|
|
46
38
|
dataframes_intraday = [intraday.intraday(codigo) for codigo in codigos_contrato]
|
|
47
39
|
dataframes_intraday = [df for df in dataframes_intraday if not df.is_empty()]
|
|
@@ -54,7 +46,7 @@ def _buscar_intraday_ou_historico(
|
|
|
54
46
|
|
|
55
47
|
def futures(
|
|
56
48
|
date: DateLike,
|
|
57
|
-
contract_code:
|
|
49
|
+
contract_code: str | list[str],
|
|
58
50
|
full_report: bool = False,
|
|
59
51
|
) -> pl.DataFrame:
|
|
60
52
|
"""Busca dados de um contrato futuro da B3 para a data de referência.
|
|
@@ -62,6 +54,11 @@ def futures(
|
|
|
62
54
|
Args:
|
|
63
55
|
date: Data de referência para consulta.
|
|
64
56
|
contract_code: Código do contrato futuro na B3 ou coleção de códigos.
|
|
57
|
+
Exemplos de códigos válidos:
|
|
58
|
+
- Juros: DI1, DDI, OC1, DAP, IAP
|
|
59
|
+
- Moedas: DOL, WDO, EUR, GBR, JAP, CNY
|
|
60
|
+
- Índices: IND, WIN, ISP, WSP
|
|
61
|
+
- Commodities: BGI, CCM, ICF, CNL, SJC, SOY, ETH, GLD
|
|
65
62
|
full_report: Se False (padrão), usa o simplified price report (SPR),
|
|
66
63
|
arquivo leve (~2 KB) com apenas preços de ajuste. Se True, usa o
|
|
67
64
|
price report completo (PR, ~2 MB) com todos os dados de negociação.
|
|
@@ -70,22 +67,15 @@ def futures(
|
|
|
70
67
|
Returns:
|
|
71
68
|
DataFrame Polars com os dados do contrato informado.
|
|
72
69
|
|
|
70
|
+
Notes:
|
|
71
|
+
Os contratos DI1, DDI, FRC, FRO, DAP, DOL, WDO, IND e WIN possuem
|
|
72
|
+
histórico pré-cacheado (desde 2018) e são retornados instantaneamente.
|
|
73
|
+
Para os demais contratos, os dados são baixados diretamente da B3 a
|
|
74
|
+
cada chamada, o que pode ser mais lento.
|
|
75
|
+
|
|
73
76
|
Examples:
|
|
74
77
|
>>> df = futures("31-05-2024", "DI1")
|
|
75
|
-
>>> {"TradeDate", "TickerSymbol", "ExpirationDate", "SettlementRate"}.issubset(
|
|
76
|
-
... set(df.columns)
|
|
77
|
-
... )
|
|
78
|
-
True
|
|
79
|
-
>>> df.shape[0] > 0
|
|
80
|
-
True
|
|
81
|
-
|
|
82
78
|
>>> df = futures("31-05-2024", "DAP")
|
|
83
|
-
>>> {"TradeDate", "TickerSymbol", "ExpirationDate", "SettlementRate"}.issubset(
|
|
84
|
-
... set(df.columns)
|
|
85
|
-
... )
|
|
86
|
-
True
|
|
87
|
-
>>> df.shape[0] > 0
|
|
88
|
-
True
|
|
89
79
|
|
|
90
80
|
Véspera de Natal e Ano Novo não têm pregão:
|
|
91
81
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import datetime as dt
|
|
2
|
-
from collections.abc import Sequence
|
|
3
2
|
|
|
4
3
|
import polars as pl
|
|
5
4
|
|
|
@@ -17,7 +16,7 @@ CONTRATOS_TAXA = {"DI1", "DAP", "DDI", "FRC", "FRO"}
|
|
|
17
16
|
|
|
18
17
|
def historical(
|
|
19
18
|
data: dt.date,
|
|
20
|
-
codigo_contrato: str |
|
|
19
|
+
codigo_contrato: str | list[str],
|
|
21
20
|
full_report: bool = False,
|
|
22
21
|
) -> pl.DataFrame:
|
|
23
22
|
"""Busca histórico de futuros priorizando o dataset PR cacheado.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
|
+
import cachetools
|
|
3
4
|
import polars as pl
|
|
4
5
|
import requests
|
|
5
6
|
|
|
@@ -49,6 +50,7 @@ def _mapa_renomeacao_intraday() -> dict[str, str]:
|
|
|
49
50
|
return {nome_orig: nome_novo for nome_orig, nome_novo, _ in COLUNAS_INTRADAY}
|
|
50
51
|
|
|
51
52
|
|
|
53
|
+
@cachetools.cached(cache=cachetools.TTLCache(maxsize=16, ttl=15))
|
|
52
54
|
@retry_padrao
|
|
53
55
|
def _buscar_json_intraday(codigo_contrato: str) -> list[dict]:
|
|
54
56
|
url = f"{URL_BASE_INTRADAY}/{codigo_contrato}"
|
|
@@ -12,7 +12,7 @@ from lxml.etree import _Element
|
|
|
12
12
|
import pyield._internal.converters as cv
|
|
13
13
|
from pyield._internal.retry import DadoIndisponivelError, retry_padrao
|
|
14
14
|
from pyield._internal.types import DateLike, any_is_empty
|
|
15
|
-
from pyield.b3._contracts import
|
|
15
|
+
from pyield.b3._contracts import normalizar_codigos_contrato
|
|
16
16
|
from pyield.b3._validar_pregao import data_negociacao_valida
|
|
17
17
|
|
|
18
18
|
registro = logging.getLogger(__name__)
|
|
@@ -236,7 +236,7 @@ def _obter_xml_price_report(data: dt.date, relatorio_completo: bool) -> bytes:
|
|
|
236
236
|
|
|
237
237
|
def fetch_price_report(
|
|
238
238
|
date: DateLike,
|
|
239
|
-
contract_code: str,
|
|
239
|
+
contract_code: str | list[str],
|
|
240
240
|
full_report: bool = False,
|
|
241
241
|
) -> pl.DataFrame:
|
|
242
242
|
"""Busca e processa o price report da B3 no site oficial.
|
|
@@ -257,8 +257,8 @@ def fetch_price_report(
|
|
|
257
257
|
Args:
|
|
258
258
|
date: Data de negociação no formato 'DD-MM-YYYY', 'DD/MM/YYYY',
|
|
259
259
|
'YYYY-MM-DD' ou objeto datetime.date.
|
|
260
|
-
contract_code: Código B3 (ex.: 'DI1',
|
|
261
|
-
'
|
|
260
|
+
contract_code: Código B3 único ou lista de códigos (ex.: 'DI1',
|
|
261
|
+
['DI1', 'DAP']). Os 3 primeiros caracteres são usados no XML.
|
|
262
262
|
full_report: Se False (padrão), usa o simplified price report (SPR),
|
|
263
263
|
arquivo leve (~2 KB) com apenas preços de ajuste. Se True, usa o
|
|
264
264
|
price report completo (PR, ~2 MB) com todos os dados de negociação.
|
|
@@ -311,7 +311,6 @@ def fetch_price_report(
|
|
|
311
311
|
- MinLimitValue (Float64): Limite mínimo de variação.
|
|
312
312
|
|
|
313
313
|
Raises:
|
|
314
|
-
ValueError: Se full_report não for bool.
|
|
315
314
|
DadoIndisponivelError: Se a data for válida, mas o endpoint não fornecer
|
|
316
315
|
arquivo para a data consultada.
|
|
317
316
|
requests.HTTPError: Se a requisição HTTP ao endpoint falhar.
|
|
@@ -322,18 +321,17 @@ def fetch_price_report(
|
|
|
322
321
|
Examples:
|
|
323
322
|
>>> import pyield as yd
|
|
324
323
|
>>> df = yd.b3.fetch_price_report("26-04-2024", "DI1")
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
True
|
|
324
|
+
|
|
325
|
+
>>> # Múltiplos contratos de uma vez
|
|
326
|
+
>>> df = yd.b3.fetch_price_report("26-04-2024", ["DI1", "DAP"])
|
|
329
327
|
|
|
330
328
|
>>> # Feriado ou fim de semana (retorna DataFrame vazio)
|
|
331
329
|
>>> df = yd.b3.fetch_price_report("25-12-2023", "DI1") # Véspera de Natal
|
|
332
330
|
>>> df.is_empty()
|
|
333
331
|
True
|
|
334
332
|
"""
|
|
335
|
-
|
|
336
|
-
if any_is_empty(date) or not
|
|
333
|
+
contratos = normalizar_codigos_contrato(contract_code)
|
|
334
|
+
if any_is_empty(date) or not contratos:
|
|
337
335
|
return pl.DataFrame()
|
|
338
336
|
|
|
339
337
|
date = cv.converter_datas(date)
|
|
@@ -343,21 +341,28 @@ def fetch_price_report(
|
|
|
343
341
|
|
|
344
342
|
try:
|
|
345
343
|
xml_bytes = _obter_xml_price_report(date, full_report)
|
|
346
|
-
|
|
344
|
+
dataframes = []
|
|
345
|
+
for contrato in contratos:
|
|
346
|
+
df = _processar_xml_extraido(xml_bytes, contrato)
|
|
347
|
+
if not df.is_empty():
|
|
348
|
+
dataframes.append(df)
|
|
349
|
+
if not dataframes:
|
|
350
|
+
return pl.DataFrame()
|
|
351
|
+
return pl.concat(dataframes).sort("TickerSymbol")
|
|
347
352
|
except (zipfile.BadZipFile, etree.XMLSyntaxError):
|
|
348
|
-
registro.exception(f"Falha ao parsear o price report de {
|
|
353
|
+
registro.exception(f"Falha ao parsear o price report de {contratos} em {date}.")
|
|
349
354
|
raise
|
|
350
355
|
except Exception:
|
|
351
356
|
tipo = "PR" if full_report else "SPR"
|
|
352
357
|
registro.exception(
|
|
353
|
-
f"ERRO CRÍTICO: Falha ao processar {
|
|
358
|
+
f"ERRO CRÍTICO: Falha ao processar {contratos} {tipo} em {date}"
|
|
354
359
|
)
|
|
355
360
|
raise
|
|
356
361
|
|
|
357
362
|
|
|
358
363
|
def read_price_report(
|
|
359
364
|
xml_bytes: bytes,
|
|
360
|
-
contract_code: str,
|
|
365
|
+
contract_code: str | list[str],
|
|
361
366
|
) -> pl.DataFrame:
|
|
362
367
|
"""Lê e processa o price report da B3 a partir do conteúdo XML bruto.
|
|
363
368
|
|
|
@@ -369,7 +374,8 @@ def read_price_report(
|
|
|
369
374
|
|
|
370
375
|
Args:
|
|
371
376
|
xml_bytes: Conteúdo do XML em bytes (já descomprimido).
|
|
372
|
-
contract_code: Código B3
|
|
377
|
+
contract_code: Código B3 único ou lista de códigos (ex.: 'DI1',
|
|
378
|
+
['DI1', 'DAP']).
|
|
373
379
|
|
|
374
380
|
Returns:
|
|
375
381
|
pl.DataFrame: DataFrame com colunas tipadas e renomeadas, ordenado por
|
|
@@ -416,8 +422,15 @@ def read_price_report(
|
|
|
416
422
|
- MaxLimitValue (Float64): Limite máximo de variação.
|
|
417
423
|
- MinLimitValue (Float64): Limite mínimo de variação.
|
|
418
424
|
"""
|
|
419
|
-
|
|
420
|
-
if any_is_empty(xml_bytes) or not
|
|
425
|
+
contratos = normalizar_codigos_contrato(contract_code)
|
|
426
|
+
if any_is_empty(xml_bytes) or not contratos:
|
|
421
427
|
return pl.DataFrame()
|
|
422
428
|
|
|
423
|
-
|
|
429
|
+
dataframes = []
|
|
430
|
+
for contrato in contratos:
|
|
431
|
+
df = _processar_xml_extraido(xml_bytes, contrato)
|
|
432
|
+
if not df.is_empty():
|
|
433
|
+
dataframes.append(df)
|
|
434
|
+
if not dataframes:
|
|
435
|
+
return pl.DataFrame()
|
|
436
|
+
return pl.concat(dataframes).sort("TickerSymbol")
|