pyield 0.49.1__tar.gz → 0.49.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.49.1 → pyield-0.49.2}/PKG-INFO +1 -1
- {pyield-0.49.1 → pyield-0.49.2}/pyield/__init__.py +2 -1
- pyield-0.49.2/pyield/b3/__init__.py +7 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/b3/boletim.py +115 -57
- {pyield-0.49.1 → pyield-0.49.2}/pyield/selic/cpm.py +2 -2
- {pyield-0.49.1 → pyield-0.49.2}/pyproject.toml +1 -1
- pyield-0.49.1/pyield/b3/__init__.py +0 -1
- {pyield-0.49.1 → pyield-0.49.2}/LICENSE +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/README.md +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/_internal/__init__.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/_internal/br_numbers.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/_internal/cache.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/_internal/converters.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/_internal/data_cache.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/_internal/retry.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/_internal/types.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/anbima/__init__.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/anbima/imaq.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/anbima/mercado_secundario.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/b3/_contratos.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/b3/_validar_pregao.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/b3/derivativos_intradia.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/b3/di_over.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/bc/__init__.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/bc/_olinda.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/bc/leiloes.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/bc/sgs.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/bc/tpf_intradia.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/bc/tpf_mensal.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/bc/vna.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/di1.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/du/__init__.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/du/core.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/du/feriados/__init__.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/du/feriados/feriados_antigos_br.txt +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/du/feriados/feriados_br.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/du/feriados/feriados_novos_br.txt +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/futuro/__init__.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/futuro/contratos.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/futuro/historico.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/futuro/intradia.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/fwd.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/interpolador.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/ipca/__init__.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/ipca/historico.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/ipca/projetado.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/lft.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/ltn.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/ntnb.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/ntnb1.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/ntnbprinc.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/ntnc.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/ntnf.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/py.typed +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/relogio.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/selic/__init__.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/selic/compromissada.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/selic/copom.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/selic/probabilities.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/tpf/__init__.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/tpf/benchmark.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/tpf/leiloes.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/tpf/pre.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/tpf/rmd.py +0 -0
- {pyield-0.49.1 → pyield-0.49.2}/pyield/tpf/utils.py +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import logging
|
|
4
4
|
from importlib.metadata import PackageNotFoundError, version
|
|
5
5
|
|
|
6
|
-
from pyield import du, ipca
|
|
6
|
+
from pyield import b3, du, ipca
|
|
7
7
|
from pyield.b3.di_over import di_over
|
|
8
8
|
from pyield import futuro, di1
|
|
9
9
|
|
|
@@ -24,6 +24,7 @@ except PackageNotFoundError:
|
|
|
24
24
|
__all__ = [
|
|
25
25
|
"__version__",
|
|
26
26
|
"agora",
|
|
27
|
+
"b3",
|
|
27
28
|
"di1",
|
|
28
29
|
"di_over",
|
|
29
30
|
"du",
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
1
|
+
"""Boletim de Negociação da B3.
|
|
2
|
+
|
|
3
|
+
Este módulo expõe helpers técnicos para buscar e ler o Price Report da B3.
|
|
4
|
+
As funções preservam o schema bruto da fonte e servem como base para
|
|
5
|
+
camadas públicas enriquecidas, como ``futuro`` e ``selic.cpm``.
|
|
6
|
+
|
|
7
|
+
Estrutura resumida de um registro do XML bruto da B3::
|
|
8
|
+
|
|
3
9
|
<PricRpt>
|
|
4
|
-
<TradDt>
|
|
5
|
-
|
|
6
|
-
</TradDt>
|
|
7
|
-
<SctyId>
|
|
8
|
-
<TckrSymb>DI1F31</TckrSymb>
|
|
9
|
-
</SctyId>
|
|
10
|
+
<TradDt><Dt>2026-04-01</Dt></TradDt>
|
|
11
|
+
<SctyId><TckrSymb>DI1F31</TckrSymb></SctyId>
|
|
10
12
|
<FinInstrmId>
|
|
11
13
|
<OthrId>
|
|
12
14
|
<Id>200000235664</Id>
|
|
13
|
-
<Tp>
|
|
14
|
-
<Prtry>8</Prtry>
|
|
15
|
-
</Tp>
|
|
15
|
+
<Tp><Prtry>8</Prtry></Tp>
|
|
16
16
|
</OthrId>
|
|
17
|
-
<PlcOfListg>
|
|
18
|
-
<MktIdrCd>BVMF</MktIdrCd>
|
|
19
|
-
</PlcOfListg>
|
|
17
|
+
<PlcOfListg><MktIdrCd>BVMF</MktIdrCd></PlcOfListg>
|
|
20
18
|
</FinInstrmId>
|
|
21
|
-
<TradDtls>
|
|
22
|
-
<TradQty>29880</TradQty>
|
|
23
|
-
</TradDtls>
|
|
19
|
+
<TradDtls><TradQty>29880</TradQty></TradDtls>
|
|
24
20
|
<FinInstrmAttrbts>
|
|
25
21
|
<MktDataStrmId>E</MktDataStrmId>
|
|
26
22
|
...
|
|
27
23
|
<MinTradLmt Ccy="BRL">12.87</MinTradLmt>
|
|
28
24
|
</FinInstrmAttrbts>
|
|
29
25
|
</PricRpt>
|
|
26
|
+
|
|
27
|
+
Ref: https://www.b3.com.br/data/files/16/70/29/9C/6219D710C8F297D7AC094EA8/Catalogo_precos_v1.3.pdf
|
|
30
28
|
"""
|
|
31
29
|
|
|
32
30
|
import datetime as dt
|
|
33
31
|
import io
|
|
32
|
+
import logging
|
|
34
33
|
import re
|
|
35
34
|
import zipfile
|
|
35
|
+
from pathlib import Path
|
|
36
36
|
|
|
37
37
|
import polars as pl
|
|
38
38
|
import requests
|
|
@@ -45,17 +45,15 @@ from pyield._internal.types import DateLike, any_is_empty
|
|
|
45
45
|
from pyield.b3._contratos import normalizar_contratos
|
|
46
46
|
from pyield.b3._validar_pregao import data_negociacao_valida
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
XPATH_PRICE_REPORT = "//ns:PricRpt"
|
|
48
|
+
__all__ = [
|
|
49
|
+
"baixar_zip",
|
|
50
|
+
"buscar",
|
|
51
|
+
"ler",
|
|
52
|
+
]
|
|
54
53
|
|
|
55
54
|
# --- Mapeamento de Colunas ---
|
|
56
55
|
# Estrutura: (id_pdf, nome_xml, tipo_polars)
|
|
57
56
|
# Esta camada base preserva os nomes originais do XML da B3.
|
|
58
|
-
# https://www.b3.com.br/data/files/16/70/29/9C/6219D710C8F297D7AC094EA8/Catalogo_precos_v1.3.pdf
|
|
59
57
|
COLUNAS_PRICE_REPORT: list[tuple[str, str, type[pl.DataType]]] = [
|
|
60
58
|
("1.00", "TradDt", pl.Date),
|
|
61
59
|
("2.01", "TckrSymb", pl.String),
|
|
@@ -101,7 +99,7 @@ COLUNAS_PRICE_REPORT: list[tuple[str, str, type[pl.DataType]]] = [
|
|
|
101
99
|
# Schema completo: nome_xml → tipo_polars. Garante ordem e tipagem constante.
|
|
102
100
|
SCHEMA_PRICE_REPORT = {nome: tipo for _, nome, tipo in COLUNAS_PRICE_REPORT}
|
|
103
101
|
|
|
104
|
-
|
|
102
|
+
logger = logging.getLogger(__name__)
|
|
105
103
|
_SESSAO = requests.Session()
|
|
106
104
|
_SESSAO.headers["User-Agent"] = (
|
|
107
105
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
|
@@ -111,24 +109,76 @@ _SESSAO.headers["User-Agent"] = (
|
|
|
111
109
|
|
|
112
110
|
@ttl_cache()
|
|
113
111
|
@retry_padrao
|
|
114
|
-
def
|
|
112
|
+
def baixar_zip(data: DateLike, boletim_completo: bool = False) -> bytes:
|
|
113
|
+
"""Baixa o ZIP bruto do Boletim de Negociação da B3.
|
|
114
|
+
|
|
115
|
+
Faz o download do Price Report da B3, no formato completo (PR) ou no
|
|
116
|
+
simplified price report (SPR), e retorna apenas ZIPs estruturalmente
|
|
117
|
+
válidos para persistência como dado bruto.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
data: Data de negociação no formato 'DD-MM-YYYY', 'DD/MM/YYYY',
|
|
121
|
+
'YYYY-MM-DD' ou objeto datetime.date.
|
|
122
|
+
boletim_completo: Se False (padrão), usa o simplified price report
|
|
123
|
+
(SPR). Se True, usa o price report completo (PR).
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
Conteúdo do ZIP externo em bytes quando o ZIP contém um XML legível.
|
|
127
|
+
Retorna ``b""`` quando a resposta não contém um ZIP válido.
|
|
128
|
+
"""
|
|
129
|
+
if any_is_empty(data):
|
|
130
|
+
return bytes()
|
|
131
|
+
|
|
132
|
+
data = cv.converter_datas(data)
|
|
115
133
|
data_str = data.strftime("%y%m%d")
|
|
116
|
-
if boletim_completo
|
|
117
|
-
|
|
118
|
-
else:
|
|
119
|
-
url = (
|
|
120
|
-
f"https://www.b3.com.br/pesquisapregao/download?filelist=SPRD{data_str}.zip"
|
|
121
|
-
)
|
|
134
|
+
prefixo = "PR" if boletim_completo else "SPRD"
|
|
135
|
+
url = f"https://www.b3.com.br/pesquisapregao/download?filelist={prefixo}{data_str}.zip"
|
|
122
136
|
|
|
123
137
|
resposta = _SESSAO.get(url, timeout=(5, 10))
|
|
124
138
|
resposta.raise_for_status()
|
|
125
139
|
|
|
126
|
-
if
|
|
140
|
+
if not _zip_valido(resposta.content):
|
|
127
141
|
return bytes()
|
|
128
142
|
return resposta.content
|
|
129
143
|
|
|
130
144
|
|
|
131
|
-
def
|
|
145
|
+
def _zip_valido(conteudo_zip: bytes) -> bool: # noqa: PLR0911
|
|
146
|
+
"""Verifica se o ZIP bruto do boletim contém um XML legível."""
|
|
147
|
+
tamanho_minimo = 1024 # ZIP válido ~2KB; 1KB detecta arquivos "sem dados"
|
|
148
|
+
if len(conteudo_zip) < tamanho_minimo:
|
|
149
|
+
logger.debug("ZIP do boletim ignorado: tamanho menor que o mínimo.")
|
|
150
|
+
return False
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
with zipfile.ZipFile(io.BytesIO(conteudo_zip), "r") as zip_externo:
|
|
154
|
+
nomes = zip_externo.namelist()
|
|
155
|
+
if not nomes:
|
|
156
|
+
logger.debug("ZIP externo do boletim está vazio.")
|
|
157
|
+
return False
|
|
158
|
+
if zip_externo.testzip() is not None:
|
|
159
|
+
logger.debug("ZIP externo do boletim contém arquivo corrompido.")
|
|
160
|
+
return False
|
|
161
|
+
|
|
162
|
+
conteudo_interno = zip_externo.read(nomes[0])
|
|
163
|
+
with zipfile.ZipFile(io.BytesIO(conteudo_interno), "r") as zip_interno:
|
|
164
|
+
nomes_xml = [
|
|
165
|
+
nome for nome in zip_interno.namelist() if nome.endswith(".xml")
|
|
166
|
+
]
|
|
167
|
+
if not nomes_xml:
|
|
168
|
+
logger.debug("ZIP interno do boletim não contém XML.")
|
|
169
|
+
return False
|
|
170
|
+
if zip_interno.testzip() is not None:
|
|
171
|
+
logger.debug("ZIP interno do boletim contém arquivo corrompido.")
|
|
172
|
+
return False
|
|
173
|
+
|
|
174
|
+
except (zipfile.BadZipFile, KeyError, OSError, RuntimeError):
|
|
175
|
+
logger.debug("ZIP do boletim inválido ou ilegível.")
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
return True
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _extrair(conteudo_zip: bytes) -> bytes:
|
|
132
182
|
"""Extrai o XML válido do ZIP aninhado do Price Report da B3.
|
|
133
183
|
|
|
134
184
|
O ZIP da B3 contém um ZIP interno, que por sua vez contém um ou
|
|
@@ -201,6 +251,7 @@ def _extrair_dados_contrato(pric_rpt: etree._Element) -> dict | None:
|
|
|
201
251
|
|
|
202
252
|
|
|
203
253
|
def _parsear_xml_registros(xml_bytes: bytes) -> list[dict]:
|
|
254
|
+
namespaces = {"ns": "urn:bvmf.217.01.xsd"}
|
|
204
255
|
analisador = etree.XMLParser(
|
|
205
256
|
ns_clean=True,
|
|
206
257
|
remove_blank_text=True,
|
|
@@ -211,7 +262,7 @@ def _parsear_xml_registros(xml_bytes: bytes) -> list[dict]:
|
|
|
211
262
|
load_dtd=False,
|
|
212
263
|
)
|
|
213
264
|
arvore = etree.parse(io.BytesIO(xml_bytes), parser=analisador)
|
|
214
|
-
resultado = arvore.xpath(
|
|
265
|
+
resultado = arvore.xpath("//ns:PricRpt", namespaces=namespaces)
|
|
215
266
|
elementos: list[etree._Element] = resultado # type: ignore[assignment]
|
|
216
267
|
registros = [
|
|
217
268
|
dados
|
|
@@ -251,25 +302,26 @@ def _filtrar_df(
|
|
|
251
302
|
return df.sort("TckrSymb")
|
|
252
303
|
|
|
253
304
|
|
|
254
|
-
def
|
|
255
|
-
dados_zip =
|
|
305
|
+
def _obter_df_boletim(data: dt.date, boletim_completo: bool) -> pl.DataFrame:
|
|
306
|
+
dados_zip = baixar_zip(data, boletim_completo)
|
|
256
307
|
if not dados_zip:
|
|
257
308
|
return pl.DataFrame()
|
|
258
|
-
xml_bytes =
|
|
309
|
+
xml_bytes = _extrair(dados_zip)
|
|
259
310
|
return _processar_xml_extraido(xml_bytes)
|
|
260
311
|
|
|
261
312
|
|
|
262
|
-
def
|
|
313
|
+
def buscar(
|
|
263
314
|
data: DateLike,
|
|
264
315
|
prefixo_ticker: str | list[str] | None = None,
|
|
265
316
|
comprimento_ticker: int | None = None,
|
|
266
317
|
boletim_completo: bool = False,
|
|
267
318
|
) -> pl.DataFrame:
|
|
268
|
-
"""Busca e processa o Boletim de
|
|
319
|
+
"""Busca e processa o Boletim de Negociação da B3 no site oficial.
|
|
269
320
|
|
|
270
|
-
Faz o download do
|
|
271
|
-
|
|
272
|
-
|
|
321
|
+
Faz o download do Price Report da B3, no formato completo (PR) ou no
|
|
322
|
+
simplified price report (SPR), extrai o XML do ZIP publicado em
|
|
323
|
+
``pesquisapregao`` e devolve um DataFrame Polars com dados brutos e
|
|
324
|
+
colunas no padrão original da B3 (nomes em inglês das tags XML).
|
|
273
325
|
|
|
274
326
|
O DataFrame retornado **não** contém colunas calculadas
|
|
275
327
|
(dias_uteis, dias_corridos, dv01, taxa_forward)
|
|
@@ -353,14 +405,14 @@ def boletim_negociacao(
|
|
|
353
405
|
etree.XMLSyntaxError: Se o XML recebido estiver malformado.
|
|
354
406
|
|
|
355
407
|
Examples:
|
|
356
|
-
>>>
|
|
357
|
-
>>> df =
|
|
408
|
+
>>> import pyield as yd
|
|
409
|
+
>>> df = yd.b3.boletim.buscar("26-04-2024", "DI1")
|
|
358
410
|
|
|
359
411
|
>>> # Múltiplos contratos de uma vez
|
|
360
|
-
>>> df =
|
|
412
|
+
>>> df = yd.b3.boletim.buscar("26-04-2024", ["DI1", "DAP"])
|
|
361
413
|
|
|
362
414
|
>>> # Feriado ou fim de semana (retorna DataFrame vazio)
|
|
363
|
-
>>> df =
|
|
415
|
+
>>> df = yd.b3.boletim.buscar("25-12-2023", "DI1")
|
|
364
416
|
>>> df.is_empty()
|
|
365
417
|
True
|
|
366
418
|
"""
|
|
@@ -372,7 +424,7 @@ def boletim_negociacao(
|
|
|
372
424
|
if not data_negociacao_valida(data):
|
|
373
425
|
return pl.DataFrame()
|
|
374
426
|
|
|
375
|
-
df =
|
|
427
|
+
df = _obter_df_boletim(data, boletim_completo)
|
|
376
428
|
if df.is_empty() or prefixo_ticker is None:
|
|
377
429
|
return df
|
|
378
430
|
|
|
@@ -382,21 +434,23 @@ def boletim_negociacao(
|
|
|
382
434
|
return _filtrar_df(df, prefixos, comprimento_ticker)
|
|
383
435
|
|
|
384
436
|
|
|
385
|
-
def
|
|
386
|
-
|
|
437
|
+
def ler(
|
|
438
|
+
fonte: bytes | Path,
|
|
387
439
|
prefixo_ticker: str | list[str] | None = None,
|
|
388
440
|
comprimento_ticker: int | None = None,
|
|
389
441
|
) -> pl.DataFrame:
|
|
390
|
-
"""Lê e processa o price report da B3 a partir do
|
|
442
|
+
"""Lê e processa o price report da B3 a partir do dado bruto.
|
|
391
443
|
|
|
392
|
-
Mesma saída de :func:`
|
|
393
|
-
|
|
444
|
+
Mesma saída de :func:`buscar`, mas recebe o dado bruto em vez de
|
|
445
|
+
baixar da rede. Aceita o ZIP externo como ``bytes`` ou ``Path``
|
|
446
|
+
(caminho para o arquivo ZIP), e também XML descomprimido como ``bytes``.
|
|
394
447
|
|
|
395
|
-
Por operar diretamente sobre o
|
|
448
|
+
Por operar diretamente sobre o dado bruto, esta função preserva a
|
|
396
449
|
terminologia da fonte para filtros de `TckrSymb`.
|
|
397
450
|
|
|
398
451
|
Args:
|
|
399
|
-
|
|
452
|
+
fonte: ZIP externo em bytes, caminho (``Path``) para um arquivo ZIP
|
|
453
|
+
no disco, ou XML já descomprimido em bytes.
|
|
400
454
|
prefixo_ticker: Prefixo do ticker B3 (ex.: 'DI1', 'DOL',
|
|
401
455
|
'CPM') ou lista de prefixos (ex.: ['DI1', 'DAP']).
|
|
402
456
|
Se None (padrão), retorna todos os ativos sem filtro.
|
|
@@ -406,11 +460,15 @@ def boletim_negociacao_ler(
|
|
|
406
460
|
|
|
407
461
|
Returns:
|
|
408
462
|
pl.DataFrame: DataFrame com as mesmas colunas documentadas em
|
|
409
|
-
:func:`
|
|
463
|
+
:func:`buscar`.
|
|
410
464
|
"""
|
|
411
|
-
if
|
|
465
|
+
conteudo = fonte.read_bytes() if isinstance(fonte, Path) else fonte
|
|
466
|
+
|
|
467
|
+
if any_is_empty(conteudo):
|
|
412
468
|
return pl.DataFrame()
|
|
413
469
|
|
|
470
|
+
xml_bytes = _extrair(conteudo) if conteudo[:4] == b"PK\x03\x04" else conteudo
|
|
471
|
+
|
|
414
472
|
df = _processar_xml_extraido(xml_bytes)
|
|
415
473
|
if df.is_empty() or prefixo_ticker is None:
|
|
416
474
|
return df
|
|
@@ -41,7 +41,7 @@ import pyield._internal.converters as cv
|
|
|
41
41
|
from pyield import du
|
|
42
42
|
from pyield._internal.retry import retry_padrao
|
|
43
43
|
from pyield._internal.types import DateLike
|
|
44
|
-
from pyield.b3
|
|
44
|
+
from pyield.b3 import boletim
|
|
45
45
|
|
|
46
46
|
logger = logging.getLogger(__name__)
|
|
47
47
|
|
|
@@ -271,7 +271,7 @@ def data(date: DateLike) -> pl.DataFrame:
|
|
|
271
271
|
return _empty_schema()
|
|
272
272
|
|
|
273
273
|
try:
|
|
274
|
-
df =
|
|
274
|
+
df = boletim.buscar(trade_date, prefixo_ticker="CPM")
|
|
275
275
|
except Exception:
|
|
276
276
|
logger.exception("CPM: falha ao baixar SPR para %s.", trade_date)
|
|
277
277
|
return _empty_schema()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
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
|
|
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
|