pyield 0.52.2__tar.gz → 0.53.1__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.52.2 → pyield-0.53.1}/PKG-INFO +3 -3
- {pyield-0.52.2 → pyield-0.53.1}/README.md +2 -2
- {pyield-0.52.2 → pyield-0.53.1}/pyield/_internal/converters.py +3 -2
- {pyield-0.52.2 → pyield-0.53.1}/pyield/b3/boletim.py +1 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/b3/derivativos_intradia.py +7 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/tpf/__init__.py +2 -4
- pyield-0.53.1/pyield/tpf/secundario/__init__.py +19 -0
- pyield-0.52.2/pyield/bc/tpf_intradia.py → pyield-0.53.1/pyield/tpf/secundario/_intradia.py +15 -24
- pyield-0.53.1/pyield/tpf/secundario/_mensal.py +310 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyproject.toml +1 -2
- pyield-0.52.2/pyield/bc/tpf_mensal.py +0 -146
- {pyield-0.52.2 → pyield-0.53.1}/LICENSE +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/__init__.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/_internal/__init__.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/_internal/br_numbers.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/_internal/cache.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/_internal/data_cache.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/_internal/retry.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/_internal/types.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/anbima/__init__.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/anbima/imaq.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/anbima/mercado_secundario.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/b3/__init__.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/b3/_contratos.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/b3/_validar_pregao.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/b3/di_over.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/bc/__init__.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/bc/_olinda.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/bc/leiloes.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/bc/lft.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/bc/sgs.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/du/__init__.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/du/core.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/du/feriados/__init__.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/du/feriados/feriados_antigos_br.txt +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/du/feriados/feriados_br.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/du/feriados/feriados_novos_br.txt +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/futuro/__init__.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/futuro/contratos.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/futuro/di1.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/futuro/historico.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/futuro/intradia.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/fwd.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/interpolador.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/ipca/__init__.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/ipca/historico.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/ipca/projetado.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/py.typed +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/relogio.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/selic/__init__.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/selic/compromissada.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/selic/copom.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/selic/cpm.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/selic/probabilities.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/tpf/_titulos/__init__.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/tpf/_titulos/lft.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/tpf/_titulos/ltn.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/tpf/_titulos/ntnb.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/tpf/_titulos/ntnb1.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/tpf/_titulos/ntnbprinc.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/tpf/_titulos/ntnc.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/tpf/_titulos/ntnf.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/tpf/benchmark.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/tpf/leiloes.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/tpf/pre.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/tpf/rmd/__init__.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/tpf/rmd/_aba_1_3.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/tpf/rmd/_aba_2_1.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/tpf/rmd/_common.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/tpf/rmd/_download.py +0 -0
- {pyield-0.52.2 → pyield-0.53.1}/pyield/tpf/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pyield
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.53.1
|
|
4
4
|
Summary: Polars-powered toolkit for Brazilian fixed income analysis
|
|
5
5
|
Keywords: fixed-income,brazil,finance,analysis,bonds
|
|
6
6
|
Author: Carlos Carvalho
|
|
@@ -113,7 +113,7 @@ documentação.
|
|
|
113
113
|
| `yd.forwards(...)` | função | Curva de taxas a termo | |
|
|
114
114
|
| `yd.futuro` | módulo | Contratos futuros da B3 | `di1`, `historico`, `intradia`, `datas_disponiveis`, `vencimento`, `enriquecer`, `vencimento_expr` |
|
|
115
115
|
| `yd.di1` | módulo | Curva DI1 e interpolação | `dados`, `interpolar_taxas`, `interpolar_taxa`, `datas_disponiveis` |
|
|
116
|
-
| `yd.tpf` | módulo | Títulos públicos federais | `taxas`, `vencimentos`, `estoque`, `leiloes`, `benchmarks`, `curva_pre`, `premio_pre`, `rmd`, `
|
|
116
|
+
| `yd.tpf` | módulo | Títulos públicos federais | `taxas`, `vencimentos`, `estoque`, `leiloes`, `benchmarks`, `curva_pre`, `premio_pre`, `rmd`, `secundario` |
|
|
117
117
|
| `yd.lft` | módulo | LFT | `dados`, `vencimentos`, `cotacao`, `pu`, `taxa`, `vna`, `rentabilidade`, `rentabilidade_expr` |
|
|
118
118
|
| `yd.ltn` | módulo | LTN | `dados`, `vencimentos`, `pu`, `taxa`, `duration_expr`, `dv01`, `dv01_expr`, `rentabilidade`, `rentabilidade_expr`, `taxas_forward` |
|
|
119
119
|
| `yd.ntnb` | módulo | NTN-B | `dados`, `vencimentos`, `datas_pagamento`, `fluxos_caixa`, `cotacao`, `pu`, `taxa`, `taxas_zero`, `duration`, `duration_expr`, `dv01`, `dv01_expr`, `implicitas`, `curva` |
|
|
@@ -305,7 +305,7 @@ fonte indisponível) retornam DataFrame vazio ou `nan`, sem lançar exceção:
|
|
|
305
305
|
import pyield as yd
|
|
306
306
|
|
|
307
307
|
yd.futuro.historico("01-01-2030", "DI1").is_empty() # -> True
|
|
308
|
-
yd.tpf.
|
|
308
|
+
yd.tpf.secundario.mensal("01-01-2030").is_empty() # -> True
|
|
309
309
|
yd.ptax("25-12-2025") # -> nan
|
|
310
310
|
```
|
|
311
311
|
|
|
@@ -69,7 +69,7 @@ documentação.
|
|
|
69
69
|
| `yd.forwards(...)` | função | Curva de taxas a termo | |
|
|
70
70
|
| `yd.futuro` | módulo | Contratos futuros da B3 | `di1`, `historico`, `intradia`, `datas_disponiveis`, `vencimento`, `enriquecer`, `vencimento_expr` |
|
|
71
71
|
| `yd.di1` | módulo | Curva DI1 e interpolação | `dados`, `interpolar_taxas`, `interpolar_taxa`, `datas_disponiveis` |
|
|
72
|
-
| `yd.tpf` | módulo | Títulos públicos federais | `taxas`, `vencimentos`, `estoque`, `leiloes`, `benchmarks`, `curva_pre`, `premio_pre`, `rmd`, `
|
|
72
|
+
| `yd.tpf` | módulo | Títulos públicos federais | `taxas`, `vencimentos`, `estoque`, `leiloes`, `benchmarks`, `curva_pre`, `premio_pre`, `rmd`, `secundario` |
|
|
73
73
|
| `yd.lft` | módulo | LFT | `dados`, `vencimentos`, `cotacao`, `pu`, `taxa`, `vna`, `rentabilidade`, `rentabilidade_expr` |
|
|
74
74
|
| `yd.ltn` | módulo | LTN | `dados`, `vencimentos`, `pu`, `taxa`, `duration_expr`, `dv01`, `dv01_expr`, `rentabilidade`, `rentabilidade_expr`, `taxas_forward` |
|
|
75
75
|
| `yd.ntnb` | módulo | NTN-B | `dados`, `vencimentos`, `datas_pagamento`, `fluxos_caixa`, `cotacao`, `pu`, `taxa`, `taxas_zero`, `duration`, `duration_expr`, `dv01`, `dv01_expr`, `implicitas`, `curva` |
|
|
@@ -261,7 +261,7 @@ fonte indisponível) retornam DataFrame vazio ou `nan`, sem lançar exceção:
|
|
|
261
261
|
import pyield as yd
|
|
262
262
|
|
|
263
263
|
yd.futuro.historico("01-01-2030", "DI1").is_empty() # -> True
|
|
264
|
-
yd.tpf.
|
|
264
|
+
yd.tpf.secundario.mensal("01-01-2030").is_empty() # -> True
|
|
265
265
|
yd.ptax("25-12-2025") # -> nan
|
|
266
266
|
```
|
|
267
267
|
|
|
@@ -52,14 +52,15 @@ def converter_datas_expr(expr: pl.Expr | str) -> pl.Expr:
|
|
|
52
52
|
expr = pl.col(expr)
|
|
53
53
|
|
|
54
54
|
# Fallback por linha: o primeiro valor não-nulo vence.
|
|
55
|
-
#
|
|
55
|
+
# Datas e datetimes Python chegam ao Polars como strings ISO.
|
|
56
56
|
# Se o elemento for null, ele permanece null em todos os termos.
|
|
57
57
|
expr_str = expr.cast(pl.String, strict=False).str.strip_chars()
|
|
58
58
|
return pl.coalesce(
|
|
59
|
-
expr.cast(pl.Date, strict=False),
|
|
60
59
|
expr_str.str.to_date(format="%d-%m-%Y", strict=False),
|
|
61
60
|
expr_str.str.to_date(format="%d/%m/%Y", strict=False),
|
|
62
61
|
expr_str.str.to_date(format="%Y-%m-%d", strict=False),
|
|
62
|
+
expr_str.str.to_date(format="%Y-%m-%d %H:%M:%S", strict=False),
|
|
63
|
+
expr_str.str.to_date(format="%Y-%m-%d %H:%M:%S%.f", strict=False),
|
|
63
64
|
)
|
|
64
65
|
|
|
65
66
|
|
|
@@ -298,6 +298,7 @@ def _converter_para_df(registros: list[dict]) -> pl.DataFrame:
|
|
|
298
298
|
# (pl.DataFrame infere colunas de ~50 primeiras linhas por padrão.)
|
|
299
299
|
schema_str = {nome: pl.String for nome in SCHEMA_PRICE_REPORT}
|
|
300
300
|
df = pl.DataFrame(registros, schema=schema_str)
|
|
301
|
+
df = df.with_columns(TradDt=pl.col("TradDt").str.to_date("%Y-%m-%d"))
|
|
301
302
|
return df.cast(SCHEMA_PRICE_REPORT, strict=False)
|
|
302
303
|
|
|
303
304
|
|
|
@@ -103,8 +103,15 @@ def _converter_json_intradia(dados_json: list[dict]) -> pl.DataFrame:
|
|
|
103
103
|
def _processar_colunas_intradia(df: pl.DataFrame) -> pl.DataFrame:
|
|
104
104
|
colunas_disponiveis = [col for col in MAPEAMENTO if col in df.columns]
|
|
105
105
|
tipos_disponiveis = pl.Schema({col: TIPOS[col] for col in colunas_disponiveis})
|
|
106
|
+
coluna_vencimento = "asset.AsstSummry.mtrtyCode"
|
|
107
|
+
exprs_data = []
|
|
108
|
+
if coluna_vencimento in colunas_disponiveis:
|
|
109
|
+
exprs_data.append(
|
|
110
|
+
pl.col(coluna_vencimento).str.to_date("%Y-%m-%d", strict=False)
|
|
111
|
+
)
|
|
106
112
|
return (
|
|
107
113
|
df.select(colunas_disponiveis)
|
|
114
|
+
.with_columns(exprs_data)
|
|
108
115
|
.cast(tipos_disponiveis, strict=False)
|
|
109
116
|
.rename(MAPEAMENTO, strict=False)
|
|
110
117
|
)
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from pyield.anbima.imaq import estoque
|
|
4
4
|
from pyield.anbima.mercado_secundario import TipoTPF, taxas, vencimentos
|
|
5
|
-
from pyield.
|
|
6
|
-
from pyield.bc.tpf_mensal import secundario_mensal
|
|
5
|
+
from pyield.tpf import secundario
|
|
7
6
|
from pyield.tpf._titulos import lft, ltn, ntnb, ntnb1, ntnbprinc, ntnc, ntnf
|
|
8
7
|
from pyield.tpf.benchmark import benchmarks
|
|
9
8
|
from pyield.tpf.leiloes import leiloes
|
|
@@ -26,8 +25,7 @@ __all__ = [
|
|
|
26
25
|
"ntnf",
|
|
27
26
|
"premio_pre",
|
|
28
27
|
"rmd",
|
|
29
|
-
"
|
|
30
|
-
"secundario_mensal",
|
|
28
|
+
"secundario",
|
|
31
29
|
"taxas",
|
|
32
30
|
"vencimentos",
|
|
33
31
|
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Negociações do mercado secundário de TPFs no sistema Selic do BCB."""
|
|
2
|
+
|
|
3
|
+
from pyield.tpf.secundario._intradia import intradia as intradia
|
|
4
|
+
from pyield.tpf.secundario._mensal import baixar_zip as baixar_zip
|
|
5
|
+
from pyield.tpf.secundario._mensal import ler_zip as ler_zip
|
|
6
|
+
from pyield.tpf.secundario._mensal import mensal as mensal
|
|
7
|
+
from pyield.tpf.secundario._mensal import (
|
|
8
|
+
nome_arquivo_mensal as nome_arquivo_mensal,
|
|
9
|
+
)
|
|
10
|
+
from pyield.tpf.secundario._mensal import zip_para_silver as zip_para_silver
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"baixar_zip",
|
|
14
|
+
"intradia",
|
|
15
|
+
"ler_zip",
|
|
16
|
+
"mensal",
|
|
17
|
+
"nome_arquivo_mensal",
|
|
18
|
+
"zip_para_silver",
|
|
19
|
+
]
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Busca dados intradiários de negociações secundárias da dívida pública federal.
|
|
3
|
-
https://www.bcb.gov.br/htms/selic/selicprecos.asp?frame=1
|
|
4
|
-
"""
|
|
1
|
+
"""Dados intradia do mercado secundário de TPFs no sistema Selic do BCB."""
|
|
5
2
|
|
|
6
3
|
import datetime as dt
|
|
7
4
|
|
|
@@ -13,30 +10,26 @@ from pyield._internal.br_numbers import float_br, inteiro_br, taxa_br
|
|
|
13
10
|
from pyield._internal.cache import ttl_cache
|
|
14
11
|
from pyield._internal.retry import retry_padrao
|
|
15
12
|
|
|
16
|
-
HORA_INICIO_TEMPO_REAL = dt.time(9, 0, 0)
|
|
17
|
-
HORA_FIM_TEMPO_REAL = dt.time(22, 0, 0)
|
|
18
13
|
URL_BASE_TEMPO_REAL = (
|
|
19
14
|
"https://www3.bcb.gov.br/novoselic/rest/precosNegociacao/pub/download/estatisticas/"
|
|
20
15
|
)
|
|
21
16
|
|
|
17
|
+
HORA_INICIO_TEMPO_REAL = dt.time(9, 0, 0)
|
|
18
|
+
HORA_FIM_TEMPO_REAL = dt.time(22, 0, 0)
|
|
19
|
+
|
|
22
20
|
|
|
23
21
|
@ttl_cache()
|
|
24
22
|
@retry_padrao
|
|
25
|
-
def
|
|
26
|
-
"""
|
|
27
|
-
Exemplo de URL do CSV com dados intradiários:
|
|
28
|
-
https://www3.bcb.gov.br/novoselic/rest/precosNegociacao/pub/download/estatisticas/02-06-2025
|
|
29
|
-
"""
|
|
23
|
+
def _buscar_csv_intradia() -> bytes:
|
|
30
24
|
hoje = relogio.hoje()
|
|
31
25
|
data_formatada = hoje.strftime("%d-%m-%Y")
|
|
32
26
|
url = f"{URL_BASE_TEMPO_REAL}{data_formatada}"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return
|
|
27
|
+
resposta = requests.get(url, timeout=30) # API costuma levar ~10s
|
|
28
|
+
resposta.raise_for_status()
|
|
29
|
+
return resposta.content
|
|
36
30
|
|
|
37
31
|
|
|
38
|
-
def
|
|
39
|
-
"""Lê CSV como strings."""
|
|
32
|
+
def _parsear_csv_intradia(dados: bytes) -> pl.DataFrame:
|
|
40
33
|
return pl.read_csv(
|
|
41
34
|
dados,
|
|
42
35
|
separator=";",
|
|
@@ -45,8 +38,7 @@ def _parsear_df(dados: bytes) -> pl.DataFrame:
|
|
|
45
38
|
).rename(lambda c: c.strip())
|
|
46
39
|
|
|
47
40
|
|
|
48
|
-
def
|
|
49
|
-
"""Filtra registros, converte tipos e reordena colunas."""
|
|
41
|
+
def _processar_df_intradia(df: pl.DataFrame) -> pl.DataFrame:
|
|
50
42
|
agora = relogio.agora()
|
|
51
43
|
return df.filter(pl.col("//1") == "1").select(
|
|
52
44
|
data_hora_consulta=agora,
|
|
@@ -84,7 +76,6 @@ def _processar_df(df: pl.DataFrame) -> pl.DataFrame:
|
|
|
84
76
|
|
|
85
77
|
|
|
86
78
|
def _mercado_selic_aberto() -> bool:
|
|
87
|
-
"""Verifica se o mercado SELIC está aberto no momento."""
|
|
88
79
|
agora = relogio.agora()
|
|
89
80
|
hoje = agora.date()
|
|
90
81
|
hora = agora.time()
|
|
@@ -94,7 +85,7 @@ def _mercado_selic_aberto() -> bool:
|
|
|
94
85
|
return eh_dia_util and eh_horario
|
|
95
86
|
|
|
96
87
|
|
|
97
|
-
def
|
|
88
|
+
def intradia() -> pl.DataFrame:
|
|
98
89
|
"""Busca dados intradia do mercado secundário de TPFs.
|
|
99
90
|
|
|
100
91
|
Fonte: Banco Central do Brasil, sistema SELIC. Os dados ficam disponíveis
|
|
@@ -138,11 +129,11 @@ def secundario_intradia() -> pl.DataFrame:
|
|
|
138
129
|
* termo_quantidade_corretagem (Int64): títulos a termo via corretagem.
|
|
139
130
|
|
|
140
131
|
Examples:
|
|
141
|
-
>>> df = yd.tpf.
|
|
132
|
+
>>> df = yd.tpf.secundario.intradia() # doctest: +SKIP
|
|
142
133
|
"""
|
|
143
134
|
if not _mercado_selic_aberto():
|
|
144
135
|
return pl.DataFrame()
|
|
145
136
|
|
|
146
|
-
texto_bruto =
|
|
147
|
-
df =
|
|
148
|
-
return
|
|
137
|
+
texto_bruto = _buscar_csv_intradia()
|
|
138
|
+
df = _parsear_csv_intradia(texto_bruto)
|
|
139
|
+
return _processar_df_intradia(df)
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"""Dados mensais do mercado secundário de TPFs no sistema Selic do BCB."""
|
|
2
|
+
|
|
3
|
+
import datetime as dt
|
|
4
|
+
import io
|
|
5
|
+
import os
|
|
6
|
+
import zipfile as zf
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import polars as pl
|
|
10
|
+
import polars.selectors as ps
|
|
11
|
+
import requests
|
|
12
|
+
|
|
13
|
+
from pyield import relogio
|
|
14
|
+
from pyield._internal.br_numbers import float_br
|
|
15
|
+
from pyield._internal.cache import ttl_cache
|
|
16
|
+
from pyield._internal.converters import converter_datas
|
|
17
|
+
from pyield._internal.retry import retry_padrao
|
|
18
|
+
from pyield._internal.types import DateLike, any_is_empty
|
|
19
|
+
|
|
20
|
+
URL_BASE_MENSAL = "https://www4.bcb.gov.br/pom/demab/negociacoes/download"
|
|
21
|
+
CHAVES_ORDENACAO = ["data_liquidacao", "titulo", "data_vencimento"]
|
|
22
|
+
COLUNAS_MINIMAS_CSV = 2
|
|
23
|
+
|
|
24
|
+
type CaminhoArquivo = str | os.PathLike[str]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _tipo_arquivo(extragrupo: bool) -> str:
|
|
28
|
+
return "E" if extragrupo else "T"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _data_mensal(data: DateLike) -> dt.date:
|
|
32
|
+
data_alvo = converter_datas(data)
|
|
33
|
+
if not isinstance(data_alvo, dt.date):
|
|
34
|
+
msg = "data deve ser escalar para consultas mensais do secundário de TPFs"
|
|
35
|
+
raise ValueError(msg)
|
|
36
|
+
return data_alvo
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def nome_arquivo_mensal(data: DateLike, extragrupo: bool = False) -> str:
|
|
40
|
+
"""Retorna o nome do arquivo ZIP mensal do secundário no BCB/SELIC.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
data: Data de referência. Apenas ano e mês definem o arquivo.
|
|
44
|
+
extragrupo: Se verdadeiro, retorna o nome do arquivo extragrupo.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Nome do arquivo ZIP mensal publicado pelo BCB.
|
|
48
|
+
|
|
49
|
+
Examples:
|
|
50
|
+
>>> yd.tpf.secundario.nome_arquivo_mensal("07-06-2026")
|
|
51
|
+
'NegT202606.ZIP'
|
|
52
|
+
>>> yd.tpf.secundario.nome_arquivo_mensal("07-01-2025", extragrupo=True)
|
|
53
|
+
'NegE202501.ZIP'
|
|
54
|
+
"""
|
|
55
|
+
data_alvo = _data_mensal(data)
|
|
56
|
+
return f"Neg{_tipo_arquivo(extragrupo)}{data_alvo:%Y%m}.ZIP"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@ttl_cache()
|
|
60
|
+
@retry_padrao
|
|
61
|
+
def _baixar_url_zip(url_arquivo: str) -> bytes:
|
|
62
|
+
resposta = requests.get(url_arquivo, allow_redirects=True, timeout=60)
|
|
63
|
+
resposta.raise_for_status()
|
|
64
|
+
return resposta.content
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def baixar_zip(data: DateLike, extragrupo: bool = False) -> bytes:
|
|
68
|
+
"""Baixa o ZIP bruto mensal de negociações secundárias de TPFs.
|
|
69
|
+
|
|
70
|
+
Fonte: Banco Central do Brasil, sistema SELIC. A fonte publica um arquivo
|
|
71
|
+
por mês; por isso, apenas o ano e o mês de ``data`` são usados.
|
|
72
|
+
|
|
73
|
+
A função valida a estrutura mínima do ZIP antes de retornar os bytes, para
|
|
74
|
+
evitar que pipelines de ingestão salvem bronze vazio, corrompido ou sem CSV
|
|
75
|
+
plausível.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
data: Data de referência. Apenas ano e mês definem o arquivo.
|
|
79
|
+
extragrupo: Se verdadeiro, baixa o arquivo extragrupo.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Bytes validados do arquivo ZIP mensal publicado pelo BCB.
|
|
83
|
+
|
|
84
|
+
Raises:
|
|
85
|
+
requests.HTTPError: Se o arquivo não estiver disponível no BCB ou a
|
|
86
|
+
resposta HTTP indicar erro.
|
|
87
|
+
ValueError: Se o conteúdo baixado não for um ZIP bruto plausível.
|
|
88
|
+
|
|
89
|
+
Examples:
|
|
90
|
+
>>> conteudo = yd.tpf.secundario.baixar_zip("07-01-2025") # doctest: +SKIP
|
|
91
|
+
"""
|
|
92
|
+
arquivo = nome_arquivo_mensal(data, extragrupo)
|
|
93
|
+
conteudo_zip = _baixar_url_zip(f"{URL_BASE_MENSAL}/{arquivo}")
|
|
94
|
+
_validar_zip(conteudo_zip, arquivo)
|
|
95
|
+
return conteudo_zip
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _extrair_csv_zip(conteudo_zip: bytes) -> bytes:
|
|
99
|
+
try:
|
|
100
|
+
arquivo_zip = zf.ZipFile(io.BytesIO(conteudo_zip), "r")
|
|
101
|
+
except zf.BadZipFile as exc:
|
|
102
|
+
msg = "ZIP inválido ou ilegível"
|
|
103
|
+
raise ValueError(msg) from exc
|
|
104
|
+
|
|
105
|
+
with arquivo_zip:
|
|
106
|
+
nomes = arquivo_zip.namelist()
|
|
107
|
+
if not nomes:
|
|
108
|
+
raise ValueError("ZIP vazio")
|
|
109
|
+
|
|
110
|
+
arquivo_corrompido = arquivo_zip.testzip()
|
|
111
|
+
if arquivo_corrompido is not None:
|
|
112
|
+
msg = f"ZIP contém arquivo corrompido: {arquivo_corrompido}"
|
|
113
|
+
raise ValueError(msg)
|
|
114
|
+
|
|
115
|
+
return arquivo_zip.read(nomes[0])
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _validar_zip(conteudo_zip: bytes, nome: str | None = None) -> None:
|
|
119
|
+
rotulo = f"{nome}: " if nome else ""
|
|
120
|
+
try:
|
|
121
|
+
conteudo_csv = _extrair_csv_zip(conteudo_zip)
|
|
122
|
+
except ValueError as exc:
|
|
123
|
+
msg = f"{rotulo}{exc}"
|
|
124
|
+
raise ValueError(msg) from exc
|
|
125
|
+
|
|
126
|
+
df_amostra = pl.read_csv(
|
|
127
|
+
conteudo_csv,
|
|
128
|
+
encoding="latin1",
|
|
129
|
+
separator=";",
|
|
130
|
+
infer_schema=False,
|
|
131
|
+
null_values="",
|
|
132
|
+
n_rows=1,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
if len(df_amostra.columns) < COLUNAS_MINIMAS_CSV:
|
|
136
|
+
msg = f"{rotulo}CSV não parece estar separado por ponto e vírgula"
|
|
137
|
+
raise ValueError(msg)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _parsear_csv_mensal(conteudo_csv: bytes) -> pl.DataFrame:
|
|
141
|
+
return pl.read_csv(
|
|
142
|
+
conteudo_csv,
|
|
143
|
+
encoding="latin1",
|
|
144
|
+
separator=";",
|
|
145
|
+
infer_schema=False,
|
|
146
|
+
null_values="",
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _processar_df_mensal(df: pl.DataFrame) -> pl.DataFrame:
|
|
151
|
+
operacoes_corretagem = (
|
|
152
|
+
pl.col("NUM OPER COM CORRETAGEM").cast(pl.Int64)
|
|
153
|
+
if "NUM OPER COM CORRETAGEM" in df.columns
|
|
154
|
+
else pl.lit(None, dtype=pl.Int64)
|
|
155
|
+
)
|
|
156
|
+
quantidade_corretagem = (
|
|
157
|
+
pl.col("QUANT NEG COM CORRETAGEM").cast(pl.Int64)
|
|
158
|
+
if "QUANT NEG COM CORRETAGEM" in df.columns
|
|
159
|
+
else pl.lit(None, dtype=pl.Int64)
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
df.with_columns(ps.string().str.strip_chars())
|
|
164
|
+
.with_columns(
|
|
165
|
+
quantidade=pl.col("QUANT NEGOCIADA").cast(pl.Int64),
|
|
166
|
+
pu_medio=float_br("PU MED"),
|
|
167
|
+
)
|
|
168
|
+
.select(
|
|
169
|
+
data_liquidacao=pl.col("DATA MOV").str.to_date("%d/%m/%Y", strict=False),
|
|
170
|
+
titulo=pl.col("SIGLA"),
|
|
171
|
+
codigo_selic=pl.col("CODIGO").cast(pl.Int64),
|
|
172
|
+
isin=pl.col("CODIGO ISIN"),
|
|
173
|
+
data_emissao=pl.col("EMISSAO").str.to_date("%d/%m/%Y", strict=False),
|
|
174
|
+
data_vencimento=pl.col("VENCIMENTO").str.to_date("%d/%m/%Y", strict=False),
|
|
175
|
+
operacoes=pl.col("NUM DE OPER").cast(pl.Int64),
|
|
176
|
+
quantidade=pl.col("quantidade"),
|
|
177
|
+
pu_minimo=float_br("PU MIN"),
|
|
178
|
+
pu_medio=pl.col("pu_medio"),
|
|
179
|
+
pu_maximo=float_br("PU MAX"),
|
|
180
|
+
pu_lastro=float_br("PU LASTRO"),
|
|
181
|
+
valor_par=float_br("VALOR PAR"),
|
|
182
|
+
taxa_minima=float_br("TAXA MIN"),
|
|
183
|
+
taxa_media=float_br("TAXA MED"),
|
|
184
|
+
taxa_maxima=float_br("TAXA MAX"),
|
|
185
|
+
operacoes_corretagem=operacoes_corretagem,
|
|
186
|
+
quantidade_corretagem=quantidade_corretagem,
|
|
187
|
+
)
|
|
188
|
+
.sort(CHAVES_ORDENACAO)
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def zip_para_silver(conteudo_zip: bytes) -> pl.DataFrame:
|
|
193
|
+
"""Converte o ZIP mensal bruto do secundário de TPFs em silver.
|
|
194
|
+
|
|
195
|
+
Fonte: Banco Central do Brasil, sistema SELIC. Esta função representa a
|
|
196
|
+
etapa bronze -> silver: extrai o CSV interno, limpa os campos, converte
|
|
197
|
+
tipos e retorna o schema Polars canônico usado pela PYield. Ela não faz
|
|
198
|
+
enriquecimento para camada ouro.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
conteudo_zip: Bytes do arquivo ZIP bruto mensal.
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
DataFrame Polars com dados mensais do mercado secundário.
|
|
205
|
+
|
|
206
|
+
Output Columns:
|
|
207
|
+
* data_liquidacao (Date): data de liquidação da negociação.
|
|
208
|
+
* titulo (String): sigla do título público.
|
|
209
|
+
* codigo_selic (Int64): código único no sistema SELIC.
|
|
210
|
+
* isin (String): código ISIN.
|
|
211
|
+
* data_emissao (Date): data de emissão do título.
|
|
212
|
+
* data_vencimento (Date): data de vencimento do título.
|
|
213
|
+
* operacoes (Int64): número total de operações.
|
|
214
|
+
* quantidade (Int64): quantidade total negociada.
|
|
215
|
+
* pu_minimo (Float64): preço unitário mínimo.
|
|
216
|
+
* pu_medio (Float64): preço unitário médio.
|
|
217
|
+
* pu_maximo (Float64): preço unitário máximo.
|
|
218
|
+
* pu_lastro (Float64): preço unitário de lastro.
|
|
219
|
+
* valor_par (Float64): valor par do título.
|
|
220
|
+
* taxa_minima (Float64): taxa mínima.
|
|
221
|
+
* taxa_media (Float64): taxa média.
|
|
222
|
+
* taxa_maxima (Float64): taxa máxima.
|
|
223
|
+
* operacoes_corretagem (Int64): operações com corretagem.
|
|
224
|
+
* quantidade_corretagem (Int64): quantidade com corretagem.
|
|
225
|
+
|
|
226
|
+
Notes:
|
|
227
|
+
O schema silver é estável para concatenação entre meses. Em layouts
|
|
228
|
+
antigos da fonte que não trazem corretagem, ``operacoes_corretagem`` e
|
|
229
|
+
``quantidade_corretagem`` são retornadas como nulas.
|
|
230
|
+
|
|
231
|
+
Examples:
|
|
232
|
+
>>> conteudo = yd.tpf.secundario.baixar_zip("07-01-2025") # doctest: +SKIP
|
|
233
|
+
>>> df = yd.tpf.secundario.zip_para_silver(conteudo) # doctest: +SKIP
|
|
234
|
+
"""
|
|
235
|
+
conteudo_csv = _extrair_csv_zip(conteudo_zip)
|
|
236
|
+
return _processar_df_mensal(_parsear_csv_mensal(conteudo_csv))
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def ler_zip(caminho: CaminhoArquivo) -> pl.DataFrame:
|
|
240
|
+
"""Lê um ZIP mensal local do secundário de TPFs e converte para silver.
|
|
241
|
+
|
|
242
|
+
Fonte: Banco Central do Brasil, sistema SELIC. Esta função é um atalho para
|
|
243
|
+
pipelines que salvam o bronze bruto e depois processam o arquivo local com o
|
|
244
|
+
mesmo schema de ``zip_para_silver``.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
caminho: Caminho do arquivo ZIP bruto.
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
DataFrame Polars com dados mensais do mercado secundário.
|
|
251
|
+
|
|
252
|
+
Examples:
|
|
253
|
+
>>> df = yd.tpf.secundario.ler_zip("NegT202501.ZIP") # doctest: +SKIP
|
|
254
|
+
"""
|
|
255
|
+
return zip_para_silver(Path(caminho).read_bytes())
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def mensal(data: DateLike, extragrupo: bool = False) -> pl.DataFrame:
|
|
259
|
+
"""Busca dados mensais do mercado secundário de TPFs.
|
|
260
|
+
|
|
261
|
+
Fonte: Banco Central do Brasil, sistema SELIC. Baixa o ZIP mensal de
|
|
262
|
+
negociações secundárias, valida o bronze bruto e retorna o DataFrame ouro.
|
|
263
|
+
Apenas o ano e o mês de ``data`` são usados para identificar o arquivo.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
data: Data de referência. Apenas ano e mês definem o arquivo.
|
|
267
|
+
extragrupo: Se verdadeiro, busca apenas negociações extragrupo.
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
DataFrame Polars com dados mensais do mercado secundário.
|
|
271
|
+
|
|
272
|
+
Output Columns:
|
|
273
|
+
* data_liquidacao (Date): data de liquidação da negociação.
|
|
274
|
+
* titulo (String): sigla do título público.
|
|
275
|
+
* codigo_selic (Int64): código único no sistema SELIC.
|
|
276
|
+
* isin (String): código ISIN.
|
|
277
|
+
* data_emissao (Date): data de emissão do título.
|
|
278
|
+
* data_vencimento (Date): data de vencimento do título.
|
|
279
|
+
* operacoes (Int64): número total de operações.
|
|
280
|
+
* quantidade (Int64): quantidade total negociada.
|
|
281
|
+
* pu_minimo (Float64): preço unitário mínimo.
|
|
282
|
+
* pu_medio (Float64): preço unitário médio.
|
|
283
|
+
* pu_maximo (Float64): preço unitário máximo.
|
|
284
|
+
* pu_lastro (Float64): preço unitário de lastro.
|
|
285
|
+
* valor_par (Float64): valor par do título.
|
|
286
|
+
* taxa_minima (Float64): taxa mínima.
|
|
287
|
+
* taxa_media (Float64): taxa média.
|
|
288
|
+
* taxa_maxima (Float64): taxa máxima.
|
|
289
|
+
* operacoes_corretagem (Int64): operações com corretagem.
|
|
290
|
+
* quantidade_corretagem (Int64): quantidade com corretagem.
|
|
291
|
+
* financeiro (Float64): valor financeiro negociado.
|
|
292
|
+
|
|
293
|
+
Notes:
|
|
294
|
+
Esta é a camada ouro mensal: retorna o schema de ``zip_para_silver``
|
|
295
|
+
acrescido de ``financeiro = quantidade * pu_medio``.
|
|
296
|
+
|
|
297
|
+
Examples:
|
|
298
|
+
>>> df = yd.tpf.secundario.mensal("07-01-2025", extragrupo=True)
|
|
299
|
+
"""
|
|
300
|
+
if any_is_empty(data):
|
|
301
|
+
return pl.DataFrame()
|
|
302
|
+
|
|
303
|
+
data_alvo = _data_mensal(data)
|
|
304
|
+
hoje = relogio.hoje()
|
|
305
|
+
if (data_alvo.year, data_alvo.month) > (hoje.year, hoje.month):
|
|
306
|
+
return pl.DataFrame()
|
|
307
|
+
|
|
308
|
+
return zip_para_silver(baixar_zip(data_alvo, extragrupo)).with_columns(
|
|
309
|
+
financeiro=(pl.col("quantidade") * pl.col("pu_medio")).round(2),
|
|
310
|
+
)
|
|
@@ -19,7 +19,7 @@ dependencies = [
|
|
|
19
19
|
"fastexcel>=0.19.0",
|
|
20
20
|
"tzdata>=2024.1; platform_system == 'Windows'",
|
|
21
21
|
]
|
|
22
|
-
version = "0.
|
|
22
|
+
version = "0.53.1"
|
|
23
23
|
|
|
24
24
|
[project.urls]
|
|
25
25
|
Homepage = "https://github.com/crdcj/PYield"
|
|
@@ -32,7 +32,6 @@ dev = [
|
|
|
32
32
|
"python-dotenv[cli]",
|
|
33
33
|
"ipykernel",
|
|
34
34
|
"pytest",
|
|
35
|
-
"pyright",
|
|
36
35
|
"ruff",
|
|
37
36
|
"mkdocs",
|
|
38
37
|
"mkdocs-material",
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Módulo para buscar dados mensais de negociações secundárias da Dívida Pública
|
|
3
|
-
Federal (TPF) registradas no sistema Selic do Banco Central do Brasil (BCB).
|
|
4
|
-
Os dados são baixados em ZIP, extraídos e carregados em um DataFrame Polars.
|
|
5
|
-
Exemplo do formato dos dados (3 primeiras linhas):
|
|
6
|
-
DATA MOV ; SIGLA; CODIGO; CODIGO ISIN ; EMISSAO ; VENCIMENTO; NUM DE OPER; QUANT NEGOCIADA; VALOR NEGOCIADO; PU MIN ; PU MED ; PU MAX ; PU LASTRO ; VALOR PAR ; TAXA MIN; TAXA MED; TAXA MAX; NUM OPER COM CORRETAGEM; QUANT NEG COM CORRETAGEM
|
|
7
|
-
02/09/2024; LFT ; 210100; BRSTNCLF1RC4; 26/10/2018; 01/03/2025; 48; 100221; ; 15288,00898200; 15292,57098100; 15302,77742100; 15285,54813387; 15288,23830700; -0,1897 ; -0,0565 ; 0,0032 ; 20; 16155
|
|
8
|
-
02/09/2024; LFT ; 210100; BRSTNCLF1RD2; 08/03/2019; 01/09/2025; 101; 230120; ; 15288,23830700; 15294,25937800; 15311,01778200; 15279,49187722; 15288,23830700; -0,1498 ; -0,0395 ; 0,0000 ; 21; 19059
|
|
9
|
-
02/09/2024; LFT ; 210100; BRSTNCLF1RE0; 06/09/2019; 01/03/2026; 88; 512642; ; 15286,63304100; 15288,20025100; 15292,77891300; 15268,60295396; 15288,23830700; -0,0198 ; 0,0002 ; 0,0071 ; 27; 121742
|
|
10
|
-
...
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
import datetime as dt
|
|
14
|
-
import io
|
|
15
|
-
import zipfile as zf
|
|
16
|
-
|
|
17
|
-
import polars as pl
|
|
18
|
-
import polars.selectors as ps
|
|
19
|
-
import requests
|
|
20
|
-
|
|
21
|
-
from pyield._internal.br_numbers import float_br
|
|
22
|
-
from pyield._internal.cache import ttl_cache
|
|
23
|
-
from pyield._internal.converters import converter_datas
|
|
24
|
-
from pyield._internal.retry import retry_padrao
|
|
25
|
-
from pyield._internal.types import DateLike, any_is_empty
|
|
26
|
-
from pyield.relogio import hoje
|
|
27
|
-
|
|
28
|
-
URL_BASE = "https://www4.bcb.gov.br/pom/demab/negociacoes/download"
|
|
29
|
-
|
|
30
|
-
CHAVES_ORDENACAO = ["data_liquidacao", "titulo", "data_vencimento"]
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def _montar_url(data_alvo: dt.date, extragrupo: bool) -> str:
|
|
34
|
-
ano_mes = data_alvo.strftime("%Y%m")
|
|
35
|
-
sufixo = "E" if extragrupo else "T"
|
|
36
|
-
return f"{URL_BASE}/Neg{sufixo}{ano_mes}.ZIP"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
@ttl_cache()
|
|
40
|
-
@retry_padrao
|
|
41
|
-
def _baixar_zip(url_arquivo: str) -> bytes:
|
|
42
|
-
"""Baixa o conteúdo ZIP e retorna os bytes."""
|
|
43
|
-
resposta = requests.get(url_arquivo, timeout=10)
|
|
44
|
-
resposta.raise_for_status()
|
|
45
|
-
return resposta.content
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def _descompactar_zip(conteudo_zip: bytes) -> bytes:
|
|
49
|
-
"""Descompacta o ZIP e retorna o conteúdo do primeiro arquivo."""
|
|
50
|
-
with zf.ZipFile(io.BytesIO(conteudo_zip), "r") as arquivo_zip:
|
|
51
|
-
return arquivo_zip.read(arquivo_zip.namelist()[0])
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def _parsear_df(conteudo_csv: bytes) -> pl.DataFrame:
|
|
55
|
-
"""Lê o CSV extraído do ZIP em DataFrame com todas as colunas como string."""
|
|
56
|
-
return pl.read_csv(
|
|
57
|
-
conteudo_csv,
|
|
58
|
-
encoding="latin1",
|
|
59
|
-
separator=";",
|
|
60
|
-
infer_schema=False,
|
|
61
|
-
null_values="",
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def _processar_df(df: pl.DataFrame) -> pl.DataFrame:
|
|
66
|
-
return (
|
|
67
|
-
df.with_columns(ps.string().str.strip_chars())
|
|
68
|
-
.with_columns(
|
|
69
|
-
quantidade=pl.col("QUANT NEGOCIADA").cast(pl.Int64),
|
|
70
|
-
pu_medio=float_br("PU MED"),
|
|
71
|
-
)
|
|
72
|
-
.select(
|
|
73
|
-
data_liquidacao=pl.col("DATA MOV").str.to_date("%d/%m/%Y", strict=False),
|
|
74
|
-
titulo=pl.col("SIGLA"),
|
|
75
|
-
codigo_selic=pl.col("CODIGO").cast(pl.Int64),
|
|
76
|
-
isin=pl.col("CODIGO ISIN"),
|
|
77
|
-
data_emissao=pl.col("EMISSAO").str.to_date("%d/%m/%Y", strict=False),
|
|
78
|
-
data_vencimento=pl.col("VENCIMENTO").str.to_date("%d/%m/%Y", strict=False),
|
|
79
|
-
operacoes=pl.col("NUM DE OPER").cast(pl.Int64),
|
|
80
|
-
quantidade=pl.col("quantidade"),
|
|
81
|
-
financeiro=(pl.col("quantidade") * pl.col("pu_medio")).round(2),
|
|
82
|
-
pu_minimo=float_br("PU MIN"),
|
|
83
|
-
pu_medio=pl.col("pu_medio"),
|
|
84
|
-
pu_maximo=float_br("PU MAX"),
|
|
85
|
-
pu_lastro=float_br("PU LASTRO"),
|
|
86
|
-
valor_par=float_br("VALOR PAR"),
|
|
87
|
-
taxa_minima=float_br("TAXA MIN"),
|
|
88
|
-
taxa_media=float_br("TAXA MED"),
|
|
89
|
-
taxa_maxima=float_br("TAXA MAX"),
|
|
90
|
-
operacoes_corretagem=pl.col("NUM OPER COM CORRETAGEM").cast(pl.Int64),
|
|
91
|
-
quantidade_corretagem=pl.col("QUANT NEG COM CORRETAGEM").cast(pl.Int64),
|
|
92
|
-
)
|
|
93
|
-
.sort(CHAVES_ORDENACAO)
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def secundario_mensal(
|
|
98
|
-
data: DateLike,
|
|
99
|
-
extragrupo: bool = False,
|
|
100
|
-
) -> pl.DataFrame:
|
|
101
|
-
"""Busca dados mensais do mercado secundário de TPFs.
|
|
102
|
-
|
|
103
|
-
Fonte: Banco Central do Brasil, sistema SELIC. Baixa o arquivo mensal de
|
|
104
|
-
negociações secundárias para o mês correspondente à data informada.
|
|
105
|
-
|
|
106
|
-
Args:
|
|
107
|
-
data: Data de referência.
|
|
108
|
-
extragrupo: Se verdadeiro, busca apenas negociações extragrupo.
|
|
109
|
-
|
|
110
|
-
Returns:
|
|
111
|
-
DataFrame Polars com dados mensais do mercado secundário.
|
|
112
|
-
|
|
113
|
-
Output Columns:
|
|
114
|
-
* data_liquidacao (Date): data de liquidação da negociação.
|
|
115
|
-
* titulo (String): sigla do título público.
|
|
116
|
-
* codigo_selic (Int64): código único no sistema SELIC.
|
|
117
|
-
* isin (String): código ISIN.
|
|
118
|
-
* data_emissao (Date): data de emissão do título.
|
|
119
|
-
* data_vencimento (Date): data de vencimento do título.
|
|
120
|
-
* operacoes (Int64): número total de operações.
|
|
121
|
-
* quantidade (Int64): quantidade total negociada.
|
|
122
|
-
* financeiro (Float64): valor financeiro negociado.
|
|
123
|
-
* pu_minimo (Float64): preço unitário mínimo.
|
|
124
|
-
* pu_medio (Float64): preço unitário médio.
|
|
125
|
-
* pu_maximo (Float64): preço unitário máximo.
|
|
126
|
-
* pu_lastro (Float64): preço unitário de lastro.
|
|
127
|
-
* valor_par (Float64): valor par do título.
|
|
128
|
-
* taxa_minima (Float64): taxa mínima.
|
|
129
|
-
* taxa_media (Float64): taxa média.
|
|
130
|
-
* taxa_maxima (Float64): taxa máxima.
|
|
131
|
-
* operacoes_corretagem (Int64): operações com corretagem.
|
|
132
|
-
* quantidade_corretagem (Int64): quantidade com corretagem.
|
|
133
|
-
|
|
134
|
-
Examples:
|
|
135
|
-
>>> df = yd.tpf.secundario_mensal("07-01-2025", extragrupo=True)
|
|
136
|
-
"""
|
|
137
|
-
if any_is_empty(data):
|
|
138
|
-
return pl.DataFrame()
|
|
139
|
-
data_alvo = converter_datas(data)
|
|
140
|
-
if (data_alvo.year, data_alvo.month) > (hoje().year, hoje().month):
|
|
141
|
-
return pl.DataFrame()
|
|
142
|
-
url = _montar_url(data_alvo, extragrupo)
|
|
143
|
-
conteudo_zip = _baixar_zip(url)
|
|
144
|
-
arquivo_extraido = _descompactar_zip(conteudo_zip)
|
|
145
|
-
df = _parsear_df(arquivo_extraido)
|
|
146
|
-
return _processar_df(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
|