PYield 0.46.6__tar.gz → 0.46.7__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.46.6 → pyield-0.46.7}/PKG-INFO +1 -1
- pyield-0.46.7/pyield/__about__.py +1 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/anbima/__init__.py +0 -2
- {pyield-0.46.6 → pyield-0.46.7}/pyield/b3/futures/intraday.py +1 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/b3/intraday_derivatives.py +31 -2
- {pyield-0.46.6 → pyield-0.46.7}/pyield/b3/price_report.py +35 -36
- pyield-0.46.6/pyield/__about__.py +0 -1
- pyield-0.46.6/pyield/anbima/difusao.py +0 -163
- {pyield-0.46.6 → pyield-0.46.7}/.gitignore +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/LICENSE +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/README.md +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/__init__.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/_internal/__init__.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/_internal/br_numbers.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/_internal/cache.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/_internal/converters.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/_internal/data_cache.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/_internal/retry.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/_internal/types.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/anbima/ettj_intraday.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/anbima/ettj_last.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/anbima/ima.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/anbima/imaq.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/anbima/tpf.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/b3/__init__.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/b3/_contracts.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/b3/_validar_pregao.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/b3/di1.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/b3/di_over.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/b3/futures/__init__.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/b3/futures/common.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/b3/futures/historical.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/bc/__init__.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/bc/auction.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/bc/copom.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/bc/ptax_api.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/bc/rates.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/bc/repo.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/bc/trades_intraday.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/bc/trades_monthly.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/bc/vna.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/bday/__init__.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/bday/core.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/bday/holidays/br_holidays_new.txt +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/bday/holidays/br_holidays_old.txt +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/bday/holidays/brholidays.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/clock.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/fwd.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/interpolator.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/ipca/__init__.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/ipca/historical.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/ipca/projected.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/py.typed +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/rmd.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/selic/__init__.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/selic/cpm.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/selic/probabilities.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/tn/__init__.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/tn/auctions.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/tn/benchmark.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/tn/lft.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/tn/ltn.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/tn/ntnb.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/tn/ntnb1.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/tn/ntnbprinc.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/tn/ntnc.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/tn/ntnf.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/tn/pre.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyield/tn/utils.py +0 -0
- {pyield-0.46.6 → pyield-0.46.7}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyield
|
|
3
|
-
Version: 0.46.
|
|
3
|
+
Version: 0.46.7
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.46.7"
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from pyield.anbima.difusao import tpf_difusao
|
|
2
1
|
from pyield.anbima.ettj_intraday import intraday_ettj
|
|
3
2
|
from pyield.anbima.ettj_last import last_ettj
|
|
4
3
|
from pyield.anbima.ima import last_ima
|
|
@@ -13,5 +12,4 @@ __all__ = [
|
|
|
13
12
|
"fetch_tpf",
|
|
14
13
|
"last_ettj",
|
|
15
14
|
"intraday_ettj",
|
|
16
|
-
"tpf_difusao",
|
|
17
15
|
]
|
|
@@ -87,6 +87,7 @@ def _intraday_contrato(codigo_contrato: str) -> pl.DataFrame:
|
|
|
87
87
|
def _preprocessar_df_intraday(df: pl.DataFrame) -> pl.DataFrame:
|
|
88
88
|
return (
|
|
89
89
|
df.filter(pl.col("codigo_mercado") == "FUT")
|
|
90
|
+
.drop_nulls(subset=["data_vencimento"])
|
|
90
91
|
.rename(
|
|
91
92
|
{
|
|
92
93
|
"preco_limite_minimo": "taxa_limite_minimo",
|
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
"""Cotações intraday de derivativos da B3 (endpoint DerivativeQuotation).
|
|
2
|
+
|
|
3
|
+
Exemplo de chamada à API:
|
|
4
|
+
https://cotacao.b3.com.br/mds/api/v1/DerivativeQuotation/DI1
|
|
5
|
+
|
|
6
|
+
Exemplo de resposta JSON (resumido):
|
|
7
|
+
{"Scty": [
|
|
8
|
+
{"symb": "DI1J30",
|
|
9
|
+
"desc": "DI DE 1 DIA",
|
|
10
|
+
"asset": {"code": "DI1",
|
|
11
|
+
"AsstSummry": {"mtrtyCode": "2030-04-01",
|
|
12
|
+
"opnCtrcts": 64037,
|
|
13
|
+
"grssAmt": 5272867.79,
|
|
14
|
+
"tradQty": 36,
|
|
15
|
+
"traddCtrctsQty": 89}},
|
|
16
|
+
"mkt": {"cd": "FUT"},
|
|
17
|
+
"SctyQtn": {"curPrc": 14.105, "opngPric": 14.22,
|
|
18
|
+
"minPric": 14.02, "maxPric": 14.22,
|
|
19
|
+
"avrgPric": 14.1013,
|
|
20
|
+
"prvsDayAdjstmntPric": 14.127,
|
|
21
|
+
"bottomLmtPric": 13.16,
|
|
22
|
+
"topLmtPric": 15.385},
|
|
23
|
+
"buyOffer": {"price": 14.105},
|
|
24
|
+
"sellOffer": {"price": 14.13}},
|
|
25
|
+
...
|
|
26
|
+
]}
|
|
27
|
+
"""
|
|
28
|
+
|
|
1
29
|
import datetime as dt
|
|
2
30
|
import logging
|
|
3
31
|
|
|
@@ -122,6 +150,8 @@ def fetch_intraday_derivatives(contract_code: str) -> pl.DataFrame:
|
|
|
122
150
|
* preco_oferta_compra (Float64): melhor oferta de compra (opcional).
|
|
123
151
|
* preco_oferta_venda (Float64): melhor oferta de venda (opcional).
|
|
124
152
|
* tipo_lado (String): tipo de lado (opcional).
|
|
153
|
+
* atualizado_as (Datetime): horário aproximado a que o dado se
|
|
154
|
+
refere (horário da consulta menos 15 min de atraso da fonte).
|
|
125
155
|
"""
|
|
126
156
|
if not intraday_disponivel():
|
|
127
157
|
return pl.DataFrame()
|
|
@@ -133,9 +163,8 @@ def fetch_intraday_derivatives(contract_code: str) -> pl.DataFrame:
|
|
|
133
163
|
return (
|
|
134
164
|
_converter_json_intraday(dados_json)
|
|
135
165
|
.pipe(_processar_colunas_intraday)
|
|
136
|
-
.drop_nulls(subset=["data_vencimento"])
|
|
137
166
|
.with_columns(
|
|
138
167
|
atualizado_as=clock.now() - dt.timedelta(minutes=15),
|
|
139
168
|
)
|
|
140
|
-
.sort("
|
|
169
|
+
.sort("codigo_negociacao")
|
|
141
170
|
)
|
|
@@ -43,7 +43,7 @@ COLUNAS_PRICE_REPORT: list[tuple[str, str, type[pl.DataType]]] = [
|
|
|
43
43
|
("3.01.01", "Id", pl.String),
|
|
44
44
|
("3.01.02.01", "Prtry", pl.String),
|
|
45
45
|
("3.02.01", "MktIdrCd", pl.String),
|
|
46
|
-
("4.01", "DaysToSttlm", pl.
|
|
46
|
+
("4.01", "DaysToSttlm", pl.Int64),
|
|
47
47
|
("4.02", "TradQty", pl.Int64),
|
|
48
48
|
("5.01", "MktDataStrmId", pl.String),
|
|
49
49
|
("5.02", "NtlFinVol", pl.Float64),
|
|
@@ -109,20 +109,23 @@ def _extrair_xml_de_zip(conteudo_zip: bytes) -> bytes:
|
|
|
109
109
|
raise ValueError("ZIP externo está vazio")
|
|
110
110
|
|
|
111
111
|
# Caso 1: ZIP contém XML diretamente
|
|
112
|
-
|
|
113
|
-
if
|
|
114
|
-
|
|
115
|
-
return zip_externo.read(xml_nomes[-1])
|
|
112
|
+
nomes_xml = sorted(n for n in nomes_externos if n.endswith(".xml"))
|
|
113
|
+
if nomes_xml:
|
|
114
|
+
return zip_externo.read(nomes_xml[-1])
|
|
116
115
|
|
|
117
|
-
# Caso 2: ZIP aninhado — o primeiro
|
|
118
|
-
|
|
116
|
+
# Caso 2: ZIP aninhado — busca o primeiro ZIP interno válido
|
|
117
|
+
for nome in nomes_externos:
|
|
118
|
+
conteudo_interno = zip_externo.read(nome)
|
|
119
|
+
if conteudo_interno[:4] != b"PK\x03\x04":
|
|
120
|
+
continue
|
|
121
|
+
with zipfile.ZipFile(io.BytesIO(conteudo_interno), "r") as zip_interno:
|
|
122
|
+
nomes_xml = sorted(
|
|
123
|
+
n for n in zip_interno.namelist() if n.endswith(".xml")
|
|
124
|
+
)
|
|
125
|
+
if nomes_xml:
|
|
126
|
+
return zip_interno.read(nomes_xml[-1])
|
|
119
127
|
|
|
120
|
-
|
|
121
|
-
xml_nomes = [n for n in zip_interno.namelist() if n.endswith(".xml")]
|
|
122
|
-
if not xml_nomes:
|
|
123
|
-
raise ValueError("Nenhum XML encontrado no ZIP")
|
|
124
|
-
xml_nomes.sort()
|
|
125
|
-
return zip_interno.read(xml_nomes[-1])
|
|
128
|
+
raise ValueError("Nenhum XML encontrado no ZIP")
|
|
126
129
|
|
|
127
130
|
|
|
128
131
|
def _ticker_valido_para_contrato(ticker: str, codigo_contrato: str) -> bool:
|
|
@@ -219,7 +222,14 @@ def _obter_xml_price_report(data: dt.date, relatorio_completo: bool) -> bytes:
|
|
|
219
222
|
dados_zip = _baixar_zip_url(data, relatorio_completo)
|
|
220
223
|
if not dados_zip:
|
|
221
224
|
return bytes()
|
|
222
|
-
|
|
225
|
+
try:
|
|
226
|
+
return _extrair_xml_de_zip(dados_zip)
|
|
227
|
+
except zipfile.BadZipFile:
|
|
228
|
+
registro.warning("ZIP corrompido na transmissão, re-baixando...")
|
|
229
|
+
dados_zip = _baixar_zip_url(data, relatorio_completo)
|
|
230
|
+
if not dados_zip:
|
|
231
|
+
return bytes()
|
|
232
|
+
return _extrair_xml_de_zip(dados_zip)
|
|
223
233
|
|
|
224
234
|
|
|
225
235
|
def fetch_price_report(
|
|
@@ -264,7 +274,7 @@ def fetch_price_report(
|
|
|
264
274
|
* Id (String): identificador do instrumento.
|
|
265
275
|
* Prtry (String): tipo do identificador proprietário.
|
|
266
276
|
* MktIdrCd (String): código do mercado.
|
|
267
|
-
* DaysToSttlm (
|
|
277
|
+
* DaysToSttlm (Int64): dias para liquidação.
|
|
268
278
|
* TradQty (Int64): número de negócios.
|
|
269
279
|
* MktDataStrmId (String): identificador do fluxo de dados.
|
|
270
280
|
* NtlFinVol (Float64): volume financeiro nacional bruto.
|
|
@@ -303,9 +313,8 @@ def fetch_price_report(
|
|
|
303
313
|
|
|
304
314
|
Raises:
|
|
305
315
|
requests.HTTPError: Se a requisição HTTP ao endpoint falhar.
|
|
306
|
-
zipfile.BadZipFile: Se o
|
|
316
|
+
zipfile.BadZipFile: Se o ZIP estiver corrompido após re-download.
|
|
307
317
|
etree.XMLSyntaxError: Se o XML recebido estiver malformado.
|
|
308
|
-
Exception: Erros críticos inesperados são propagados após log.
|
|
309
318
|
|
|
310
319
|
Examples:
|
|
311
320
|
>>> import pyield as yd
|
|
@@ -328,25 +337,15 @@ def fetch_price_report(
|
|
|
328
337
|
if not data_negociacao_valida(date):
|
|
329
338
|
return pl.DataFrame()
|
|
330
339
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
return pl.concat(dataframes, how="diagonal").sort("TckrSymb")
|
|
341
|
-
except (zipfile.BadZipFile, etree.XMLSyntaxError):
|
|
342
|
-
registro.exception(f"Falha ao parsear o price report de {contratos} em {date}.")
|
|
343
|
-
raise
|
|
344
|
-
except Exception:
|
|
345
|
-
tipo = "PR" if full_report else "SPR"
|
|
346
|
-
registro.exception(
|
|
347
|
-
f"ERRO CRÍTICO: Falha ao processar {contratos} {tipo} em {date}"
|
|
348
|
-
)
|
|
349
|
-
raise
|
|
340
|
+
xml_bytes = _obter_xml_price_report(date, full_report)
|
|
341
|
+
dataframes = []
|
|
342
|
+
for contrato in contratos:
|
|
343
|
+
df = _processar_xml_extraido(xml_bytes, contrato)
|
|
344
|
+
if not df.is_empty():
|
|
345
|
+
dataframes.append(df)
|
|
346
|
+
if not dataframes:
|
|
347
|
+
return pl.DataFrame()
|
|
348
|
+
return pl.concat(dataframes, how="diagonal").sort("TckrSymb")
|
|
350
349
|
|
|
351
350
|
|
|
352
351
|
def read_price_report(
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.46.6"
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import datetime as dt
|
|
2
|
-
import logging
|
|
3
|
-
|
|
4
|
-
import polars as pl
|
|
5
|
-
import requests
|
|
6
|
-
|
|
7
|
-
import pyield._internal.converters as cv
|
|
8
|
-
from pyield import bday
|
|
9
|
-
from pyield._internal.br_numbers import taxa_br
|
|
10
|
-
from pyield._internal.cache import ttl_cache
|
|
11
|
-
from pyield._internal.types import DateLike, any_is_empty
|
|
12
|
-
|
|
13
|
-
VERSAO_API = "1.0018"
|
|
14
|
-
URL_BASE = (
|
|
15
|
-
f"https://www.anbima.com.br/sistemas/taxasonline/consulta/versao/{VERSAO_API}"
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
URL_PAGINA_INICIAL = f"{URL_BASE}/taxasOnline.asp"
|
|
19
|
-
URL_CONSULTA_DADOS = f"{URL_BASE}/exibedados.asp"
|
|
20
|
-
URL_DOWNLOAD = f"{URL_BASE}/download_dados.asp?extensao=csv"
|
|
21
|
-
|
|
22
|
-
logger = logging.getLogger(__name__)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@ttl_cache()
|
|
26
|
-
def _buscar_dados_url(data_referencia: str) -> bytes:
|
|
27
|
-
cabecalhos = {
|
|
28
|
-
"Referer": URL_PAGINA_INICIAL,
|
|
29
|
-
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", # noqa
|
|
30
|
-
"Origin": "https://www.anbima.com.br",
|
|
31
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
32
|
-
}
|
|
33
|
-
carga = {
|
|
34
|
-
"dataref": data_referencia,
|
|
35
|
-
"dtRefIdioma": data_referencia,
|
|
36
|
-
"layoutimprimir": "0",
|
|
37
|
-
"idioma": "",
|
|
38
|
-
"idresumo": "",
|
|
39
|
-
"nome": "",
|
|
40
|
-
"provedor": "",
|
|
41
|
-
"codigo": "",
|
|
42
|
-
"vencimento": "",
|
|
43
|
-
"referencia": "",
|
|
44
|
-
"dbNome": "",
|
|
45
|
-
"fldColunas": ["C2", "C17", "C18", "C19"],
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
# Timeouts (conexão, leitura) ajustados ao perfil de cada request:
|
|
49
|
-
# - GET inicial: página leve, apenas para estabelecer cookies de sessão.
|
|
50
|
-
# - POST consulta: dispara a query no servidor (~30s observado em uso normal).
|
|
51
|
-
# - POST download: dados já preparados pelo POST anterior, resposta rápida.
|
|
52
|
-
timeout_pagina = (10, 20)
|
|
53
|
-
timeout_consulta = (10, 120)
|
|
54
|
-
timeout_download = (10, 30)
|
|
55
|
-
|
|
56
|
-
with requests.Session() as s:
|
|
57
|
-
s.get(URL_PAGINA_INICIAL, headers=cabecalhos, timeout=timeout_pagina)
|
|
58
|
-
try:
|
|
59
|
-
response_consulta = s.post(
|
|
60
|
-
URL_CONSULTA_DADOS,
|
|
61
|
-
headers=cabecalhos,
|
|
62
|
-
data=carga,
|
|
63
|
-
timeout=timeout_consulta,
|
|
64
|
-
)
|
|
65
|
-
response_consulta.raise_for_status()
|
|
66
|
-
response_download = s.post(
|
|
67
|
-
URL_DOWNLOAD,
|
|
68
|
-
headers=cabecalhos,
|
|
69
|
-
data=carga,
|
|
70
|
-
timeout=timeout_download,
|
|
71
|
-
)
|
|
72
|
-
response_download.raise_for_status()
|
|
73
|
-
if "text/html" in response_download.headers.get("Content-Type", ""):
|
|
74
|
-
raise ValueError(
|
|
75
|
-
"Servidor respondeu com HTML em vez de CSV para a data "
|
|
76
|
-
f"{data_referencia}."
|
|
77
|
-
)
|
|
78
|
-
response_download.encoding = "iso-8859-1"
|
|
79
|
-
return response_download.text.encode("utf-8")
|
|
80
|
-
except requests.exceptions.RequestException as e:
|
|
81
|
-
logger.error(f"Erro ao buscar dados para a data '{data_referencia}': {e}")
|
|
82
|
-
raise
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def _ler_csv_bruto(csv_bruto: bytes) -> pl.DataFrame:
|
|
86
|
-
"""Lê o CSV bruto da Anbima e devolve DataFrame com todas as colunas string."""
|
|
87
|
-
return pl.read_csv(
|
|
88
|
-
csv_bruto,
|
|
89
|
-
separator=";",
|
|
90
|
-
infer_schema=False,
|
|
91
|
-
skip_rows=1,
|
|
92
|
-
).with_columns(pl.all().str.strip_chars())
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def _processar_csv(data_ref: dt.date, df: pl.DataFrame) -> pl.DataFrame:
|
|
96
|
-
"""Converte o DataFrame bruto em DataFrame Polars estruturado."""
|
|
97
|
-
|
|
98
|
-
return (
|
|
99
|
-
df.with_columns(
|
|
100
|
-
data_vencimento=pl.col("Vencimento").str.to_date(format="%d/%m/%Y"),
|
|
101
|
-
horario=pl.when(pl.col("Provedor") == "ANBIMA 12H")
|
|
102
|
-
.then(dt.time(12, 0))
|
|
103
|
-
.otherwise(pl.col("Horário").str.to_time(format="%H:%M:%S")),
|
|
104
|
-
taxa_venda=taxa_br("Oferta Venda"),
|
|
105
|
-
taxa_compra=taxa_br("Oferta Compra"),
|
|
106
|
-
)
|
|
107
|
-
.select(
|
|
108
|
-
data_hora_referencia=pl.lit(data_ref).dt.combine(pl.col("horario")),
|
|
109
|
-
provedor=pl.col("Provedor"),
|
|
110
|
-
titulo=pl.col("Título"),
|
|
111
|
-
data_vencimento=pl.col("data_vencimento"),
|
|
112
|
-
isin=pl.col("Código ISIN"),
|
|
113
|
-
dias_uteis=bday.count_expr(data_ref, "data_vencimento"),
|
|
114
|
-
taxa_indicativa_anterior=taxa_br("Fech D-1"),
|
|
115
|
-
taxa_indicativa_superior=taxa_br("Indicativo Superior"),
|
|
116
|
-
taxa_indicativa_inferior=taxa_br("Indicativo Inferior"),
|
|
117
|
-
taxa_venda=pl.col("taxa_venda"),
|
|
118
|
-
taxa_compra=pl.col("taxa_compra"),
|
|
119
|
-
taxa_media=pl.mean_horizontal("taxa_compra", "taxa_venda"),
|
|
120
|
-
taxa_ultima=taxa_br("Última"),
|
|
121
|
-
)
|
|
122
|
-
.sort("titulo", "data_vencimento", "data_hora_referencia")
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
def tpf_difusao(data_referencia: DateLike) -> pl.DataFrame:
|
|
127
|
-
"""
|
|
128
|
-
Obtém a TPF Difusão da Anbima para uma data de referência específica.
|
|
129
|
-
|
|
130
|
-
Args:
|
|
131
|
-
data_referencia (DateLike): Data de referência.
|
|
132
|
-
|
|
133
|
-
Returns:
|
|
134
|
-
pl.DataFrame: DataFrame com os dados.
|
|
135
|
-
|
|
136
|
-
Raises:
|
|
137
|
-
requests.RequestException: Em falhas operacionais de rede ou HTTP.
|
|
138
|
-
ValueError: Em resposta inválida da API ou erro de parsing do CSV.
|
|
139
|
-
|
|
140
|
-
Output Columns:
|
|
141
|
-
- data_hora_referencia (Datetime): Data e hora de referência da taxa.
|
|
142
|
-
- provedor (string): Provedor dos dados.
|
|
143
|
-
- titulo (string): Nome do título (ex: LFT, LTN).
|
|
144
|
-
- data_vencimento (date): Data de vencimento do título.
|
|
145
|
-
- isin (string): código ISIN do título.
|
|
146
|
-
- dias_uteis (int): Dias úteis entre a data de referência e o vencimento.
|
|
147
|
-
- taxa_indicativa_anterior (float): Taxa indicativa de fechamento D-1 (decimal).
|
|
148
|
-
- taxa_indicativa_superior (float): Limite superior da banda indicativa (decimal).
|
|
149
|
-
- taxa_indicativa_inferior (float): Limite inferior da banda indicativa (decimal).
|
|
150
|
-
- taxa_venda (float): Taxa de oferta de venda (Ask rate) (decimal).
|
|
151
|
-
- taxa_compra (float): Taxa de oferta de compra (Bid rate) (decimal).
|
|
152
|
-
- taxa_media (float): Média entre a taxa de compra e venda (decimal).
|
|
153
|
-
- taxa_ultima (float): Última taxa negociada (decimal).
|
|
154
|
-
"""
|
|
155
|
-
if any_is_empty(data_referencia):
|
|
156
|
-
return pl.DataFrame()
|
|
157
|
-
data = cv.converter_datas(data_referencia)
|
|
158
|
-
if not bday.is_business_day(data):
|
|
159
|
-
return pl.DataFrame()
|
|
160
|
-
data_str = data.strftime("%d/%m/%Y")
|
|
161
|
-
csv_bruto = _buscar_dados_url(data_str)
|
|
162
|
-
df = _ler_csv_bruto(csv_bruto)
|
|
163
|
-
return _processar_csv(data, df)
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|