pyield 0.59.0__tar.gz → 0.60.0__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.
Files changed (96) hide show
  1. pyield-0.60.0/PKG-INFO +221 -0
  2. pyield-0.60.0/README.md +196 -0
  3. pyield-0.60.0/pyield/_internal/calendario_igpm.py +115 -0
  4. pyield-0.60.0/pyield/_internal/igpm.py +138 -0
  5. {pyield-0.59.0 → pyield-0.60.0}/pyield/_internal/numbers.py +6 -1
  6. {pyield-0.59.0 → pyield-0.60.0}/pyield/bc/sgs.py +38 -10
  7. pyield-0.60.0/pyield/igpm.py +8 -0
  8. {pyield-0.59.0 → pyield-0.60.0}/pyield/ipca/__init__.py +2 -0
  9. pyield-0.60.0/pyield/ipca/_calendario.py +105 -0
  10. {pyield-0.59.0 → pyield-0.60.0}/pyield/ipca/historico.py +55 -36
  11. {pyield-0.59.0 → pyield-0.60.0}/pyield/ipca/projetado.py +9 -3
  12. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/titulos/_ntnb_bootstrap.py +48 -21
  13. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/titulos/lft.py +106 -15
  14. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/titulos/ltn.py +161 -24
  15. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/titulos/ntnb.py +164 -27
  16. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/titulos/ntnc.py +189 -20
  17. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/titulos/ntnf.py +281 -38
  18. {pyield-0.59.0 → pyield-0.60.0}/pyield/vna/__init__.py +4 -0
  19. pyield-0.60.0/pyield/vna/_calculo.py +176 -0
  20. pyield-0.60.0/pyield/vna/_consulta.py +461 -0
  21. pyield-0.60.0/pyield/vna/_contexto.py +157 -0
  22. pyield-0.60.0/pyield/vna/_download.py +29 -0
  23. {pyield-0.59.0 → pyield-0.60.0}/pyield/vna/_lft.py +5 -6
  24. {pyield-0.59.0 → pyield-0.60.0}/pyield/vna/_ntnb.py +42 -43
  25. {pyield-0.59.0 → pyield-0.60.0}/pyield/vna/_ntnc.py +35 -32
  26. {pyield-0.59.0 → pyield-0.60.0}/pyproject.toml +2 -2
  27. {pyield-0.59.0 → pyield-0.60.0}/pyproject.toml.orig +2 -2
  28. pyield-0.59.0/PKG-INFO +0 -190
  29. pyield-0.59.0/README.md +0 -165
  30. pyield-0.59.0/pyield/.DS_Store +0 -0
  31. pyield-0.59.0/pyield/igpm.py +0 -6
  32. pyield-0.59.0/pyield/vna/_calculo.py +0 -100
  33. pyield-0.59.0/pyield/vna/_consulta.py +0 -244
  34. pyield-0.59.0/pyield/vna/_download.py +0 -53
  35. {pyield-0.59.0 → pyield-0.60.0}/LICENSE +0 -0
  36. {pyield-0.59.0 → pyield-0.60.0}/pyield/__init__.py +0 -0
  37. {pyield-0.59.0 → pyield-0.60.0}/pyield/_internal/__init__.py +0 -0
  38. {pyield-0.59.0 → pyield-0.60.0}/pyield/_internal/br_numbers.py +0 -0
  39. {pyield-0.59.0 → pyield-0.60.0}/pyield/_internal/cache.py +0 -0
  40. {pyield-0.59.0 → pyield-0.60.0}/pyield/_internal/converters.py +0 -0
  41. {pyield-0.59.0 → pyield-0.60.0}/pyield/_internal/data_cache.py +0 -0
  42. {pyield-0.59.0 → pyield-0.60.0}/pyield/_internal/excel.py +0 -0
  43. {pyield-0.59.0 → pyield-0.60.0}/pyield/_internal/retry.py +0 -0
  44. {pyield-0.59.0 → pyield-0.60.0}/pyield/_internal/types.py +0 -0
  45. {pyield-0.59.0 → pyield-0.60.0}/pyield/anbima/__init__.py +0 -0
  46. {pyield-0.59.0 → pyield-0.60.0}/pyield/anbima/imaq.py +0 -0
  47. {pyield-0.59.0 → pyield-0.60.0}/pyield/anbima/taxas.py +0 -0
  48. {pyield-0.59.0 → pyield-0.60.0}/pyield/b3/__init__.py +0 -0
  49. {pyield-0.59.0 → pyield-0.60.0}/pyield/b3/_contratos.py +0 -0
  50. {pyield-0.59.0 → pyield-0.60.0}/pyield/b3/_validar_pregao.py +0 -0
  51. {pyield-0.59.0 → pyield-0.60.0}/pyield/b3/boletim.py +0 -0
  52. {pyield-0.59.0 → pyield-0.60.0}/pyield/b3/derivativos_intradia.py +0 -0
  53. {pyield-0.59.0 → pyield-0.60.0}/pyield/b3/di_over.py +0 -0
  54. {pyield-0.59.0 → pyield-0.60.0}/pyield/bc/__init__.py +0 -0
  55. {pyield-0.59.0 → pyield-0.60.0}/pyield/bc/_olinda.py +0 -0
  56. {pyield-0.59.0 → pyield-0.60.0}/pyield/compromissada.py +0 -0
  57. {pyield-0.59.0 → pyield-0.60.0}/pyield/copom/__init__.py +0 -0
  58. {pyield-0.59.0 → pyield-0.60.0}/pyield/copom/_calendario.py +0 -0
  59. {pyield-0.59.0 → pyield-0.60.0}/pyield/cpm/__init__.py +0 -0
  60. {pyield-0.59.0 → pyield-0.60.0}/pyield/cpm/_contratos.py +0 -0
  61. {pyield-0.59.0 → pyield-0.60.0}/pyield/cpm/_probabilidades.py +0 -0
  62. {pyield-0.59.0 → pyield-0.60.0}/pyield/du/__init__.py +0 -0
  63. {pyield-0.59.0 → pyield-0.60.0}/pyield/du/core.py +0 -0
  64. {pyield-0.59.0 → pyield-0.60.0}/pyield/du/feriados_br/__init__.py +0 -0
  65. {pyield-0.59.0 → pyield-0.60.0}/pyield/du/feriados_br/anteriores.txt +0 -0
  66. {pyield-0.59.0 → pyield-0.60.0}/pyield/du/feriados_br/atuais.txt +0 -0
  67. {pyield-0.59.0 → pyield-0.60.0}/pyield/futuro/__init__.py +0 -0
  68. {pyield-0.59.0 → pyield-0.60.0}/pyield/futuro/contratos.py +0 -0
  69. {pyield-0.59.0 → pyield-0.60.0}/pyield/futuro/di1.py +0 -0
  70. {pyield-0.59.0 → pyield-0.60.0}/pyield/futuro/historico.py +0 -0
  71. {pyield-0.59.0 → pyield-0.60.0}/pyield/futuro/intradia.py +0 -0
  72. {pyield-0.59.0 → pyield-0.60.0}/pyield/fwd.py +0 -0
  73. {pyield-0.59.0 → pyield-0.60.0}/pyield/interpolador.py +0 -0
  74. {pyield-0.59.0 → pyield-0.60.0}/pyield/py.typed +0 -0
  75. {pyield-0.59.0 → pyield-0.60.0}/pyield/relogio.py +0 -0
  76. {pyield-0.59.0 → pyield-0.60.0}/pyield/rmd/__init__.py +0 -0
  77. {pyield-0.59.0 → pyield-0.60.0}/pyield/rmd/_aba_1_3.py +0 -0
  78. {pyield-0.59.0 → pyield-0.60.0}/pyield/rmd/_aba_2_1.py +0 -0
  79. {pyield-0.59.0 → pyield-0.60.0}/pyield/rmd/_common.py +0 -0
  80. {pyield-0.59.0 → pyield-0.60.0}/pyield/rmd/_download.py +0 -0
  81. {pyield-0.59.0 → pyield-0.60.0}/pyield/selic/__init__.py +0 -0
  82. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/__init__.py +0 -0
  83. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/_leiloes_bcb.py +0 -0
  84. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/_taxas.py +0 -0
  85. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/benchmark.py +0 -0
  86. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/dealers.py +0 -0
  87. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/leiloes.py +0 -0
  88. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/secundario/__init__.py +0 -0
  89. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/secundario/_intradia.py +0 -0
  90. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/secundario/_mensal.py +0 -0
  91. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/titulos/__init__.py +0 -0
  92. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/titulos/_ntnf_bootstrap.py +0 -0
  93. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/titulos/_utils.py +0 -0
  94. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/titulos/ntnb1.py +0 -0
  95. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/titulos/ntnbp.py +0 -0
  96. {pyield-0.59.0 → pyield-0.60.0}/pyield/tpf/titulos/pre.py +0 -0
pyield-0.60.0/PKG-INFO ADDED
@@ -0,0 +1,221 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyield
3
+ Version: 0.60.0
4
+ Summary: Polars-powered toolkit for Brazilian fixed income analysis
5
+ Keywords: fixed-income,brazil,finance,analysis,bonds
6
+ Author: Carlos Carvalho
7
+ Author-email: Carlos Carvalho <carlos.carvalho.jr@gmail.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Intended Audience :: Financial and Insurance Industry
13
+ Classifier: Topic :: Office/Business :: Financial :: Investment
14
+ Requires-Dist: polars>=1.39.0
15
+ Requires-Dist: lxml>=6.0.0
16
+ Requires-Dist: requests>=2.31.0
17
+ Requires-Dist: fastexcel>=0.19.0
18
+ Requires-Dist: tzdata>=2024.1 ; sys_platform == 'win32'
19
+ Requires-Python: >=3.12
20
+ Project-URL: Homepage, https://github.com/crdcj/PYield
21
+ Project-URL: Documentation, https://crdcj.github.io/PYield
22
+ Project-URL: Source, https://github.com/crdcj/PYield
23
+ Project-URL: Bug Tracker, https://github.com/crdcj/PYield/issues
24
+ Description-Content-Type: text/markdown
25
+
26
+ [![PyPI version](https://img.shields.io/pypi/v/pyield.svg)](https://pypi.python.org/pypi/pyield)
27
+ [![Made with Python](https://img.shields.io/badge/Python->=3.12-blue?logo=python&logoColor=white)](https://python.org "Go to Python homepage")
28
+ [![Powered by Polars](https://img.shields.io/badge/Powered%20by-Polars-blue)](https://pola.rs/)
29
+ [![License](https://img.shields.io/badge/License-MIT-blue)](https://github.com/crdcj/PYield/blob/main/LICENSE)
30
+ [![Docs](https://img.shields.io/badge/docs-GitHub%20Pages-blue?logo=readthedocs&logoColor=white)](https://crdcj.github.io/PYield/)
31
+
32
+ # PYield: Toolkit de Renda Fixa Brasileira
33
+
34
+ Português | [English](https://github.com/crdcj/PYield/blob/main/README.en.md)
35
+
36
+ PYield é uma biblioteca Python voltada para análise de títulos públicos
37
+ brasileiros. Ela busca e processa dados da ANBIMA, BCB, IBGE, B3 e Tesouro
38
+ Nacional, retornando tipos nativos do Python, `polars.Series` ou
39
+ `polars.DataFrame`, conforme a operação.
40
+
41
+ Embora inclua dados e ferramentas de outros mercados, como DI1, DAP e PTAX,
42
+ esses recursos apoiam o objetivo central da biblioteca: análise, precificação e
43
+ acompanhamento de títulos públicos brasileiros.
44
+
45
+ ## Instalação
46
+
47
+ Com `pip`:
48
+
49
+ ```sh
50
+ pip install pyield
51
+ ```
52
+
53
+ Em um projeto gerenciado pelo `uv`:
54
+
55
+ ```sh
56
+ uv add pyield
57
+ ```
58
+
59
+ ## Próximos passos
60
+
61
+ - [Quickstart](https://crdcj.github.io/PYield/quickstart/): primeiros usos da biblioteca.
62
+ - [Documentação completa](https://crdcj.github.io/PYield/): conceitos e referência por módulo.
63
+ - [Mapa da API](https://crdcj.github.io/PYield/api-map/): namespaces e funções públicas.
64
+ - [Desenvolvimento e publicação](https://crdcj.github.io/PYield/desenvolvimento/): ambiente, verificações, build e PyPI.
65
+ - [Gerar e publicar a documentação](docs/desenvolvimento.md#documentação): geração, visualização local e GitHub Pages.
66
+ - [Notebook no Colab](https://colab.research.google.com/github/crdcj/PYield/blob/main/examples/pyield_quickstart.ipynb): exploração interativa.
67
+ - [Pacote no PyPI](https://pypi.org/project/pyield/).
68
+
69
+ ## Mapa da API
70
+
71
+ | Componente | Tipo | Finalidade | Funções públicas |
72
+ |---|---|---|---|
73
+ | `yd.du` | módulo | Dias úteis e calendário brasileiro | `contar`, `deslocar`, `eh_dia_util`, `gerar`, `ultimo_dia_util`, `contar_expr`, `deslocar_expr`, `eh_dia_util_expr` |
74
+ | `yd.Interpolador` | classe | Interpolação escalar e em pipelines Polars, com métodos `linear` e `flat_forward` | `interpolar`, `interpolar_expr` |
75
+ | `yd.interpolar(...)` | função | Interpolação vetorizada flat-forward, curva única ou multi-curva | |
76
+ | `yd.forward(...)` | função | Taxa a termo entre dois vértices | |
77
+ | `yd.forwards(...)` | função | Curva de taxas a termo | |
78
+ | `yd.futuro` | módulo | Contratos futuros da B3 | `di1`, `historico`, `intradia`, `datas_disponiveis`, `vencimento`, `enriquecer`, `vencimento_expr` |
79
+ | `yd.di1` | módulo | Curva DI1 e interpolação | `dados`, `interpolar_taxas`, `interpolar_taxa`, `datas_disponiveis` |
80
+ | `yd.tpf` | módulo | Títulos públicos federais | `taxas`, `taxas_historicas`, `vencimentos`, `estoque`, `leiloes`, `leiloes_bcb`, `benchmarks`, `dealers`, `curva_pre`, `premios_pre`, `secundario` |
81
+ | `yd.rmd(aba)` | função | Relatório Mensal da Dívida do Tesouro Nacional | |
82
+ | `yd.lft` | módulo | LFT | `dados`, `vencimentos`, `cotacao`, `pu`, `taxa`, `rentabilidade`, `rentabilidade_expr` |
83
+ | `yd.ltn` | módulo | LTN | `dados`, `vencimentos`, `pu`, `taxa`, `duration_expr`, `dv01`, `dv01_expr`, `rentabilidade`, `rentabilidade_expr`, `taxas_forward` |
84
+ | `yd.ntnb` | módulo | NTN-B | `dados`, `vencimentos`, `datas_pagamento`, `fluxos_caixa`, `cotacao`, `pu`, `taxa`, `taxas_zero`, `duration`, `duration_expr`, `dv01`, `dv01_expr`, `implicitas` |
85
+ | `yd.ntnb1` | módulo | NTN-B1 (Educa+ e Renda+) | `NomeComercial`, `datas_pagamento`, `fluxos_caixa`, `cotacao`, `cotacao_curva_zero`, `taxa_curva_zero`, `pu`, `duration`, `dv01` |
86
+ | `yd.ntnbp` | módulo | NTN-B Principal | `cotacao`, `taxa`, `pu`, `dv01` |
87
+ | `yd.ntnc` | módulo | NTN-C | `dados`, `datas_pagamento`, `fluxos_caixa`, `cotacao`, `cotacao_curva_zero`, `taxa_curva_zero`, `pu`, `taxa`, `duration`, `duration_expr`, `dv01`, `dv01_expr` |
88
+ | `yd.ntnf` | módulo | NTN-F | `dados`, `vencimentos`, `datas_pagamento`, `fluxos_caixa`, `pu`, `taxa`, `taxas_zero`, `taxas_zero_forwards`, `premio_limpo`, `premio_limpo_expr`, `rentabilidade`, `rentabilidade_expr`, `duration`, `duration_expr`, `dv01`, `dv01_expr` |
89
+ | `yd.vna` | módulo | Valores nominais atualizados dos títulos públicos | `valor`, `historico`, `ultimo`, `projetado`, `vigencia`, `contexto`, `calcular_vna`, `calcular_base` |
90
+ | `yd.copom` | módulo | Calendário automático do Copom | `calendario`, `proxima_reuniao` |
91
+ | `yd.compromissadas(inicio, fim)` | função | Leilões de operações compromissadas do BCB | |
92
+ | `yd.selic` | módulo | Selic e política monetária | `over`, `over_serie`, `meta`, `meta_serie` |
93
+ | `yd.cpm` | módulo | Opções digitais do COPOM e análises derivadas | `contratos`, `probabilidades` |
94
+ | `yd.ipca` | módulo | IPCA histórico e projetado | `indice`, `indice_serie`, `taxa`, `taxa_serie`, `taxa_projetada`, `calendario` |
95
+ | `yd.igpm` | módulo | Taxas e números-índice do IGP-M | `taxa`, `taxa_serie`, `indice`, `indice_serie`, `calendario` |
96
+ | `yd.ptax(data)` | função | PTAX para uma data | |
97
+ | `yd.ptax_serie(inicio, fim)` | função | Série histórica da PTAX | |
98
+ | `yd.di_over(data)` | função | Taxa DI Over | |
99
+ | `yd.hoje()` | função | Data atual no Brasil | |
100
+ | `yd.agora()` | função | Data e hora atual no Brasil | |
101
+
102
+ O [mapa completo da API](https://crdcj.github.io/PYield/api-map/) inclui a
103
+ documentação detalhada e as assinaturas públicas.
104
+
105
+ ## Novidades na versão 0.60.0
106
+
107
+ Esta seção cobre apenas as mudanças da `0.59.0` para a `0.60.0`. Consulte também as
108
+ [notas de migração](https://crdcj.github.io/PYield/release-0.60.0/).
109
+
110
+ ### Contratos com Decimal
111
+
112
+ Os históricos e cálculos de VNA e os indicadores de inflação passam a preservar
113
+ precisão decimal. Revise operações que misturam esses retornos com `float`:
114
+ use `Decimal("0.0045")` para constantes exatas e `.is_nan()` para verificar
115
+ um retorno escalar `Decimal("NaN")`. Converta para `float` explicitamente
116
+ apenas quando a aplicação aceitar perder essa precisão.
117
+
118
+ | Operação | Retorno na 0.60.0 |
119
+ |---|---|
120
+ | `vna.valor`, `vna.projetado`, `vna.calcular_vna`, `vna.calcular_base` | `Decimal` |
121
+ | Coluna `vna` de `vna.historico` e `vna.ultimo` | `Decimal(18, 6)` |
122
+ | `ipca.indice` / coluna `indice` de `ipca.indice_serie` | `Decimal` / `Decimal(18, 2)` |
123
+ | `ipca.taxa` / coluna `taxa` de `ipca.taxa_serie` | `Decimal` / `Decimal(18, 4)` |
124
+ | `ipca.taxa_projetada().valor_projetado` | `Decimal` com quatro casas |
125
+ | `igpm.taxa` / coluna `taxa` de `igpm.taxa_serie` | `Decimal` / `Decimal(18, 4)` |
126
+ | `igpm.indice` / coluna `indice` de `igpm.indice_serie` | `Decimal` / `Decimal(18, 10)` |
127
+
128
+ `vna.calcular_vna` também passa de `float("nan")` para `Decimal("NaN")`
129
+ na ausência de dados. Os cálculos de VNA usam 28 dígitos de precisão e aplicam
130
+ os truncamentos e arredondamentos da metodologia; resultados intermediários
131
+ podem diferir dos obtidos anteriormente com `float`.
132
+
133
+ ### Séries do IPCA
134
+
135
+ As consultas históricas do IPCA seguem o padrão das séries de Selic e PTAX:
136
+ `taxa(data)` e `indice(data)` retornam um valor; `taxa_serie` e `indice_serie`
137
+ retornam séries por intervalo ou pelos últimos meses.
138
+
139
+ Para migrar, substitua `ipca.taxas(inicio, fim)` por
140
+ `ipca.taxa_serie(inicio, fim)` e `ipca.indices(inicio, fim)` por
141
+ `ipca.indice_serie(inicio, fim)`. Substitua `ipca.taxas_ultimas(n)` por
142
+ `ipca.taxa_serie(ultimos=n)` e `ipca.indices_ultimos(n)` por
143
+ `ipca.indice_serie(ultimos=n)`. Os nomes antigos foram removidos. As colunas e unidades são preservadas;
144
+ os tipos numéricos mudam conforme descrito acima.
145
+
146
+ O número-índice agora usa `Decimal`: `ipca.indice(data)` retorna `Decimal`
147
+ (ou `Decimal("NaN")` quando indisponível), e a coluna `indice` de
148
+ `ipca.indice_serie(...)` usa `Decimal(18, 2)`. A conversão parte do texto do
149
+ IBGE, sem passar por `float`.
150
+
151
+ As taxas também usam `Decimal`: `ipca.taxa(data)` retorna `Decimal` (ou
152
+ `Decimal("NaN")`), e a coluna `taxa` de `ipca.taxa_serie(...)` usa
153
+ `Decimal(18, 4)`. O campo `valor_projetado` de `ipca.taxa_projetada()` passa a
154
+ ser `Decimal` com quatro casas. A unidade permanece decimal: 0,45% corresponde
155
+ a `Decimal("0.0045")`.
156
+
157
+ Nas séries, `fim=None` usa a data atual no Brasil. Informe `inicio` ou
158
+ `ultimos`; quando informado, `ultimos` tem prioridade sobre as datas.
159
+
160
+ ### Contexto de VNA e calendários de inflação
161
+
162
+ - `yd.vna.contexto(...)` reúne os parâmetros da vigência de NTN-B ou NTN-C:
163
+ VNAs publicados, competência, inflação e pendências. Exige `vencimento` para
164
+ NTN-C e aceita uma projeção explícita quando a inflação realizada não existe.
165
+ As entradas de inflação em `contexto` e `projetado` aceitam números decimais
166
+ (`0.0045`) ou percentuais explícitos (`"0.45%"` ou `"0,45%"`). A coluna
167
+ `inflacao` de `contexto` usa Decimal e pode ser passada diretamente a
168
+ `projetado`.
169
+ Migração: substitua o antigo `inflacao=0.45` por `inflacao="0.45%"` ou
170
+ `inflacao=0.0045`; números puros agora representam taxas decimais.
171
+ - `yd.ipca.calendario(ano, atualizar=False)` consulta as datas previstas de
172
+ divulgação no IBGE pelo ano da competência, incluindo dezembro divulgado no
173
+ ano seguinte.
174
+ - `yd.igpm.calendario(ano, atualizar=False)` consulta as divulgações mensais
175
+ na FGV IBRE, excluindo prévias decendiais. Os calendários não comprovam
176
+ que os índices já foram publicados.
177
+
178
+ ### Outras mudanças
179
+
180
+ - `yd.vna.calcular_base(titulo, indice_atual, vencimento=None)` calcula a base
181
+ mensal da NTN-B ou NTN-C com o número-índice realizado do IPCA ou IGP-M,
182
+ ou o VNA da LFT com o fator Selic acumulado desde 01/07/2000, sem acesso
183
+ à rede. As origens e os índices iniciais são constantes internas;
184
+ NTN-C exige vencimento para selecionar a série e seu ajuste de emissão.
185
+ Para LFT, arredonda o fator em dezesseis casas; para NTN-B e NTN-C, trunca
186
+ o fator nessa precisão. Trunca o VNA em seis casas, preservando a
187
+ precisão da origem e evitando encadear VNAs mensais já truncados.
188
+ - Novo módulo `yd.igpm`, com `taxa` e `taxa_serie` para consultar taxas mensais
189
+ do IGP-M via SGS/BCB. `taxa` retorna `Decimal` (ou `Decimal("NaN")` quando
190
+ indisponível), e a coluna `taxa` de `taxa_serie` usa `Decimal(18, 4)`.
191
+ A unidade é decimal: 0,27% corresponde a `Decimal("0.0027")`.
192
+ `indice` e `indice_serie` fornecem os números-índice da FGV via Ipeadata
193
+ (série `IGP12_IGPM12`), como `Decimal` e coluna `Decimal(18, 10)`.
194
+ As dez casas preservam o histórico anterior a 1994. As séries aceitam
195
+ intervalo ou `ultimos`, com competência `periodo` no formato `YYYYMM`.
196
+ - No CPM, substitua `yd.cpm.data(data)` por `yd.cpm.contratos(data)` e use
197
+ `yd.cpm.probabilidades(data, tipo_opcao="call")` para as probabilidades.
198
+ Esta última retorna todas as reuniões disponíveis; filtre o DataFrame para
199
+ selecionar uma reunião.
200
+ - Leilões do BCB passam de `yd.bc.leiloes` para `yd.tpf.leiloes_bcb`, com nomes
201
+ de colunas alinhados aos de `yd.tpf.leiloes`.
202
+ - No mercado secundário, `yd.tpf.secundario.ler(fonte)` substitui `ler_zip` e
203
+ `zip_para_silver`: aceita caminho ou bytes de ZIP e inclui `financeiro` na
204
+ saída. `nome_arquivo_mensal` deixa de ser público.
205
+ - `yd.ntnf.premio` foi removida. Use `yd.tpf.premios_pre(data)` e filtre
206
+ `titulo == "NTN-F"` para obter o prêmio bruto.
207
+ - `yd.tpf.taxas` e as consultas `dados` de LFT, LTN, NTN-B, NTN-C e NTN-F
208
+ preservam colunas e tipos quando não há dados. Nos leilões do Tesouro, PU
209
+ mínimo informado como zero passa a ser nulo, e o financeiro ofertado usa o
210
+ PU médio calculado quando necessário.
211
+ - `yd.rmd` passa a propagar erros de download e processamento, em vez de
212
+ retornar um DataFrame vazio nessas situações.
213
+
214
+ Consulte as [releases do GitHub](https://github.com/crdcj/PYield/releases)
215
+ para o histórico completo e as notas de migração de versões anteriores.
216
+
217
+ ## Projeto
218
+
219
+ - [Código-fonte](https://github.com/crdcj/PYield)
220
+ - [Issues](https://github.com/crdcj/PYield/issues)
221
+ - [Licença MIT](LICENSE)
@@ -0,0 +1,196 @@
1
+ [![PyPI version](https://img.shields.io/pypi/v/pyield.svg)](https://pypi.python.org/pypi/pyield)
2
+ [![Made with Python](https://img.shields.io/badge/Python->=3.12-blue?logo=python&logoColor=white)](https://python.org "Go to Python homepage")
3
+ [![Powered by Polars](https://img.shields.io/badge/Powered%20by-Polars-blue)](https://pola.rs/)
4
+ [![License](https://img.shields.io/badge/License-MIT-blue)](https://github.com/crdcj/PYield/blob/main/LICENSE)
5
+ [![Docs](https://img.shields.io/badge/docs-GitHub%20Pages-blue?logo=readthedocs&logoColor=white)](https://crdcj.github.io/PYield/)
6
+
7
+ # PYield: Toolkit de Renda Fixa Brasileira
8
+
9
+ Português | [English](https://github.com/crdcj/PYield/blob/main/README.en.md)
10
+
11
+ PYield é uma biblioteca Python voltada para análise de títulos públicos
12
+ brasileiros. Ela busca e processa dados da ANBIMA, BCB, IBGE, B3 e Tesouro
13
+ Nacional, retornando tipos nativos do Python, `polars.Series` ou
14
+ `polars.DataFrame`, conforme a operação.
15
+
16
+ Embora inclua dados e ferramentas de outros mercados, como DI1, DAP e PTAX,
17
+ esses recursos apoiam o objetivo central da biblioteca: análise, precificação e
18
+ acompanhamento de títulos públicos brasileiros.
19
+
20
+ ## Instalação
21
+
22
+ Com `pip`:
23
+
24
+ ```sh
25
+ pip install pyield
26
+ ```
27
+
28
+ Em um projeto gerenciado pelo `uv`:
29
+
30
+ ```sh
31
+ uv add pyield
32
+ ```
33
+
34
+ ## Próximos passos
35
+
36
+ - [Quickstart](https://crdcj.github.io/PYield/quickstart/): primeiros usos da biblioteca.
37
+ - [Documentação completa](https://crdcj.github.io/PYield/): conceitos e referência por módulo.
38
+ - [Mapa da API](https://crdcj.github.io/PYield/api-map/): namespaces e funções públicas.
39
+ - [Desenvolvimento e publicação](https://crdcj.github.io/PYield/desenvolvimento/): ambiente, verificações, build e PyPI.
40
+ - [Gerar e publicar a documentação](docs/desenvolvimento.md#documentação): geração, visualização local e GitHub Pages.
41
+ - [Notebook no Colab](https://colab.research.google.com/github/crdcj/PYield/blob/main/examples/pyield_quickstart.ipynb): exploração interativa.
42
+ - [Pacote no PyPI](https://pypi.org/project/pyield/).
43
+
44
+ ## Mapa da API
45
+
46
+ | Componente | Tipo | Finalidade | Funções públicas |
47
+ |---|---|---|---|
48
+ | `yd.du` | módulo | Dias úteis e calendário brasileiro | `contar`, `deslocar`, `eh_dia_util`, `gerar`, `ultimo_dia_util`, `contar_expr`, `deslocar_expr`, `eh_dia_util_expr` |
49
+ | `yd.Interpolador` | classe | Interpolação escalar e em pipelines Polars, com métodos `linear` e `flat_forward` | `interpolar`, `interpolar_expr` |
50
+ | `yd.interpolar(...)` | função | Interpolação vetorizada flat-forward, curva única ou multi-curva | |
51
+ | `yd.forward(...)` | função | Taxa a termo entre dois vértices | |
52
+ | `yd.forwards(...)` | função | Curva de taxas a termo | |
53
+ | `yd.futuro` | módulo | Contratos futuros da B3 | `di1`, `historico`, `intradia`, `datas_disponiveis`, `vencimento`, `enriquecer`, `vencimento_expr` |
54
+ | `yd.di1` | módulo | Curva DI1 e interpolação | `dados`, `interpolar_taxas`, `interpolar_taxa`, `datas_disponiveis` |
55
+ | `yd.tpf` | módulo | Títulos públicos federais | `taxas`, `taxas_historicas`, `vencimentos`, `estoque`, `leiloes`, `leiloes_bcb`, `benchmarks`, `dealers`, `curva_pre`, `premios_pre`, `secundario` |
56
+ | `yd.rmd(aba)` | função | Relatório Mensal da Dívida do Tesouro Nacional | |
57
+ | `yd.lft` | módulo | LFT | `dados`, `vencimentos`, `cotacao`, `pu`, `taxa`, `rentabilidade`, `rentabilidade_expr` |
58
+ | `yd.ltn` | módulo | LTN | `dados`, `vencimentos`, `pu`, `taxa`, `duration_expr`, `dv01`, `dv01_expr`, `rentabilidade`, `rentabilidade_expr`, `taxas_forward` |
59
+ | `yd.ntnb` | módulo | NTN-B | `dados`, `vencimentos`, `datas_pagamento`, `fluxos_caixa`, `cotacao`, `pu`, `taxa`, `taxas_zero`, `duration`, `duration_expr`, `dv01`, `dv01_expr`, `implicitas` |
60
+ | `yd.ntnb1` | módulo | NTN-B1 (Educa+ e Renda+) | `NomeComercial`, `datas_pagamento`, `fluxos_caixa`, `cotacao`, `cotacao_curva_zero`, `taxa_curva_zero`, `pu`, `duration`, `dv01` |
61
+ | `yd.ntnbp` | módulo | NTN-B Principal | `cotacao`, `taxa`, `pu`, `dv01` |
62
+ | `yd.ntnc` | módulo | NTN-C | `dados`, `datas_pagamento`, `fluxos_caixa`, `cotacao`, `cotacao_curva_zero`, `taxa_curva_zero`, `pu`, `taxa`, `duration`, `duration_expr`, `dv01`, `dv01_expr` |
63
+ | `yd.ntnf` | módulo | NTN-F | `dados`, `vencimentos`, `datas_pagamento`, `fluxos_caixa`, `pu`, `taxa`, `taxas_zero`, `taxas_zero_forwards`, `premio_limpo`, `premio_limpo_expr`, `rentabilidade`, `rentabilidade_expr`, `duration`, `duration_expr`, `dv01`, `dv01_expr` |
64
+ | `yd.vna` | módulo | Valores nominais atualizados dos títulos públicos | `valor`, `historico`, `ultimo`, `projetado`, `vigencia`, `contexto`, `calcular_vna`, `calcular_base` |
65
+ | `yd.copom` | módulo | Calendário automático do Copom | `calendario`, `proxima_reuniao` |
66
+ | `yd.compromissadas(inicio, fim)` | função | Leilões de operações compromissadas do BCB | |
67
+ | `yd.selic` | módulo | Selic e política monetária | `over`, `over_serie`, `meta`, `meta_serie` |
68
+ | `yd.cpm` | módulo | Opções digitais do COPOM e análises derivadas | `contratos`, `probabilidades` |
69
+ | `yd.ipca` | módulo | IPCA histórico e projetado | `indice`, `indice_serie`, `taxa`, `taxa_serie`, `taxa_projetada`, `calendario` |
70
+ | `yd.igpm` | módulo | Taxas e números-índice do IGP-M | `taxa`, `taxa_serie`, `indice`, `indice_serie`, `calendario` |
71
+ | `yd.ptax(data)` | função | PTAX para uma data | |
72
+ | `yd.ptax_serie(inicio, fim)` | função | Série histórica da PTAX | |
73
+ | `yd.di_over(data)` | função | Taxa DI Over | |
74
+ | `yd.hoje()` | função | Data atual no Brasil | |
75
+ | `yd.agora()` | função | Data e hora atual no Brasil | |
76
+
77
+ O [mapa completo da API](https://crdcj.github.io/PYield/api-map/) inclui a
78
+ documentação detalhada e as assinaturas públicas.
79
+
80
+ ## Novidades na versão 0.60.0
81
+
82
+ Esta seção cobre apenas as mudanças da `0.59.0` para a `0.60.0`. Consulte também as
83
+ [notas de migração](https://crdcj.github.io/PYield/release-0.60.0/).
84
+
85
+ ### Contratos com Decimal
86
+
87
+ Os históricos e cálculos de VNA e os indicadores de inflação passam a preservar
88
+ precisão decimal. Revise operações que misturam esses retornos com `float`:
89
+ use `Decimal("0.0045")` para constantes exatas e `.is_nan()` para verificar
90
+ um retorno escalar `Decimal("NaN")`. Converta para `float` explicitamente
91
+ apenas quando a aplicação aceitar perder essa precisão.
92
+
93
+ | Operação | Retorno na 0.60.0 |
94
+ |---|---|
95
+ | `vna.valor`, `vna.projetado`, `vna.calcular_vna`, `vna.calcular_base` | `Decimal` |
96
+ | Coluna `vna` de `vna.historico` e `vna.ultimo` | `Decimal(18, 6)` |
97
+ | `ipca.indice` / coluna `indice` de `ipca.indice_serie` | `Decimal` / `Decimal(18, 2)` |
98
+ | `ipca.taxa` / coluna `taxa` de `ipca.taxa_serie` | `Decimal` / `Decimal(18, 4)` |
99
+ | `ipca.taxa_projetada().valor_projetado` | `Decimal` com quatro casas |
100
+ | `igpm.taxa` / coluna `taxa` de `igpm.taxa_serie` | `Decimal` / `Decimal(18, 4)` |
101
+ | `igpm.indice` / coluna `indice` de `igpm.indice_serie` | `Decimal` / `Decimal(18, 10)` |
102
+
103
+ `vna.calcular_vna` também passa de `float("nan")` para `Decimal("NaN")`
104
+ na ausência de dados. Os cálculos de VNA usam 28 dígitos de precisão e aplicam
105
+ os truncamentos e arredondamentos da metodologia; resultados intermediários
106
+ podem diferir dos obtidos anteriormente com `float`.
107
+
108
+ ### Séries do IPCA
109
+
110
+ As consultas históricas do IPCA seguem o padrão das séries de Selic e PTAX:
111
+ `taxa(data)` e `indice(data)` retornam um valor; `taxa_serie` e `indice_serie`
112
+ retornam séries por intervalo ou pelos últimos meses.
113
+
114
+ Para migrar, substitua `ipca.taxas(inicio, fim)` por
115
+ `ipca.taxa_serie(inicio, fim)` e `ipca.indices(inicio, fim)` por
116
+ `ipca.indice_serie(inicio, fim)`. Substitua `ipca.taxas_ultimas(n)` por
117
+ `ipca.taxa_serie(ultimos=n)` e `ipca.indices_ultimos(n)` por
118
+ `ipca.indice_serie(ultimos=n)`. Os nomes antigos foram removidos. As colunas e unidades são preservadas;
119
+ os tipos numéricos mudam conforme descrito acima.
120
+
121
+ O número-índice agora usa `Decimal`: `ipca.indice(data)` retorna `Decimal`
122
+ (ou `Decimal("NaN")` quando indisponível), e a coluna `indice` de
123
+ `ipca.indice_serie(...)` usa `Decimal(18, 2)`. A conversão parte do texto do
124
+ IBGE, sem passar por `float`.
125
+
126
+ As taxas também usam `Decimal`: `ipca.taxa(data)` retorna `Decimal` (ou
127
+ `Decimal("NaN")`), e a coluna `taxa` de `ipca.taxa_serie(...)` usa
128
+ `Decimal(18, 4)`. O campo `valor_projetado` de `ipca.taxa_projetada()` passa a
129
+ ser `Decimal` com quatro casas. A unidade permanece decimal: 0,45% corresponde
130
+ a `Decimal("0.0045")`.
131
+
132
+ Nas séries, `fim=None` usa a data atual no Brasil. Informe `inicio` ou
133
+ `ultimos`; quando informado, `ultimos` tem prioridade sobre as datas.
134
+
135
+ ### Contexto de VNA e calendários de inflação
136
+
137
+ - `yd.vna.contexto(...)` reúne os parâmetros da vigência de NTN-B ou NTN-C:
138
+ VNAs publicados, competência, inflação e pendências. Exige `vencimento` para
139
+ NTN-C e aceita uma projeção explícita quando a inflação realizada não existe.
140
+ As entradas de inflação em `contexto` e `projetado` aceitam números decimais
141
+ (`0.0045`) ou percentuais explícitos (`"0.45%"` ou `"0,45%"`). A coluna
142
+ `inflacao` de `contexto` usa Decimal e pode ser passada diretamente a
143
+ `projetado`.
144
+ Migração: substitua o antigo `inflacao=0.45` por `inflacao="0.45%"` ou
145
+ `inflacao=0.0045`; números puros agora representam taxas decimais.
146
+ - `yd.ipca.calendario(ano, atualizar=False)` consulta as datas previstas de
147
+ divulgação no IBGE pelo ano da competência, incluindo dezembro divulgado no
148
+ ano seguinte.
149
+ - `yd.igpm.calendario(ano, atualizar=False)` consulta as divulgações mensais
150
+ na FGV IBRE, excluindo prévias decendiais. Os calendários não comprovam
151
+ que os índices já foram publicados.
152
+
153
+ ### Outras mudanças
154
+
155
+ - `yd.vna.calcular_base(titulo, indice_atual, vencimento=None)` calcula a base
156
+ mensal da NTN-B ou NTN-C com o número-índice realizado do IPCA ou IGP-M,
157
+ ou o VNA da LFT com o fator Selic acumulado desde 01/07/2000, sem acesso
158
+ à rede. As origens e os índices iniciais são constantes internas;
159
+ NTN-C exige vencimento para selecionar a série e seu ajuste de emissão.
160
+ Para LFT, arredonda o fator em dezesseis casas; para NTN-B e NTN-C, trunca
161
+ o fator nessa precisão. Trunca o VNA em seis casas, preservando a
162
+ precisão da origem e evitando encadear VNAs mensais já truncados.
163
+ - Novo módulo `yd.igpm`, com `taxa` e `taxa_serie` para consultar taxas mensais
164
+ do IGP-M via SGS/BCB. `taxa` retorna `Decimal` (ou `Decimal("NaN")` quando
165
+ indisponível), e a coluna `taxa` de `taxa_serie` usa `Decimal(18, 4)`.
166
+ A unidade é decimal: 0,27% corresponde a `Decimal("0.0027")`.
167
+ `indice` e `indice_serie` fornecem os números-índice da FGV via Ipeadata
168
+ (série `IGP12_IGPM12`), como `Decimal` e coluna `Decimal(18, 10)`.
169
+ As dez casas preservam o histórico anterior a 1994. As séries aceitam
170
+ intervalo ou `ultimos`, com competência `periodo` no formato `YYYYMM`.
171
+ - No CPM, substitua `yd.cpm.data(data)` por `yd.cpm.contratos(data)` e use
172
+ `yd.cpm.probabilidades(data, tipo_opcao="call")` para as probabilidades.
173
+ Esta última retorna todas as reuniões disponíveis; filtre o DataFrame para
174
+ selecionar uma reunião.
175
+ - Leilões do BCB passam de `yd.bc.leiloes` para `yd.tpf.leiloes_bcb`, com nomes
176
+ de colunas alinhados aos de `yd.tpf.leiloes`.
177
+ - No mercado secundário, `yd.tpf.secundario.ler(fonte)` substitui `ler_zip` e
178
+ `zip_para_silver`: aceita caminho ou bytes de ZIP e inclui `financeiro` na
179
+ saída. `nome_arquivo_mensal` deixa de ser público.
180
+ - `yd.ntnf.premio` foi removida. Use `yd.tpf.premios_pre(data)` e filtre
181
+ `titulo == "NTN-F"` para obter o prêmio bruto.
182
+ - `yd.tpf.taxas` e as consultas `dados` de LFT, LTN, NTN-B, NTN-C e NTN-F
183
+ preservam colunas e tipos quando não há dados. Nos leilões do Tesouro, PU
184
+ mínimo informado como zero passa a ser nulo, e o financeiro ofertado usa o
185
+ PU médio calculado quando necessário.
186
+ - `yd.rmd` passa a propagar erros de download e processamento, em vez de
187
+ retornar um DataFrame vazio nessas situações.
188
+
189
+ Consulte as [releases do GitHub](https://github.com/crdcj/PYield/releases)
190
+ para o histórico completo e as notas de migração de versões anteriores.
191
+
192
+ ## Projeto
193
+
194
+ - [Código-fonte](https://github.com/crdcj/PYield)
195
+ - [Issues](https://github.com/crdcj/PYield/issues)
196
+ - [Licença MIT](LICENSE)
@@ -0,0 +1,115 @@
1
+ """Calendário de divulgação do IGP-M publicado pela FGV IBRE."""
2
+
3
+ import datetime as dt
4
+ import re
5
+ import ssl
6
+
7
+ import polars as pl
8
+ import requests
9
+ from lxml import html
10
+ from requests.adapters import HTTPAdapter
11
+
12
+ from pyield._internal.cache import ttl_cache
13
+ from pyield._internal.retry import retry_padrao
14
+
15
+ _URL = "https://portalibre.fgv.br/calendario-de-divulgacao"
16
+
17
+
18
+ class _AdaptadorFGV(HTTPAdapter):
19
+ def init_poolmanager(self, *args, **kwargs):
20
+ contexto = ssl.create_default_context()
21
+ # O servidor da FGV negocia TLS 1.2 com RSA e AES-GCM, sem ECDHE.
22
+ cifras = ":".join(cifra["name"] for cifra in contexto.get_ciphers())
23
+ contexto.set_ciphers(f"{cifras}:AES128-GCM-SHA256")
24
+ kwargs["ssl_context"] = contexto
25
+ return super().init_poolmanager(*args, **kwargs)
26
+
27
+
28
+ @ttl_cache()
29
+ @retry_padrao
30
+ def _buscar_calendario(ano: int) -> bytes:
31
+ with requests.Session() as sessao:
32
+ sessao.mount(_URL, _AdaptadorFGV())
33
+ resposta = sessao.get(
34
+ _URL,
35
+ params={
36
+ "field_indice_target_id": "128",
37
+ "field_divulgacao_data_value": f"{ano}-01-01",
38
+ "field_divulgacao_data_value_1": f"{ano}-12-31",
39
+ },
40
+ timeout=30,
41
+ )
42
+ resposta.raise_for_status()
43
+ return resposta.content
44
+
45
+
46
+ def _parsear_calendario(conteudo: bytes) -> pl.DataFrame:
47
+ linhas = []
48
+ for item in html.fromstring(conteudo).iter("li"):
49
+ texto = " ".join(" ".join(str(trecho) for trecho in item.itertext()).split())
50
+ if not re.search(r"\bIGP-M e os componentes:", texto):
51
+ continue
52
+ data = re.search(r"\b\d{2}/\d{2}/\d{4}\b", texto)
53
+ if data is None:
54
+ raise ValueError("Divulgação do IGP-M sem data válida.")
55
+ linhas.append(data[0])
56
+ return pl.DataFrame(
57
+ {"data_divulgacao": linhas}, schema={"data_divulgacao": pl.String}
58
+ )
59
+
60
+
61
+ def _processar_calendario(dados: pl.DataFrame) -> pl.DataFrame:
62
+ calendario = (
63
+ dados.with_columns(
64
+ data_divulgacao=pl.col("data_divulgacao").str.to_date("%d/%m/%Y")
65
+ )
66
+ .select(
67
+ competencia=pl.col("data_divulgacao").dt.month_start(),
68
+ data_divulgacao=pl.col("data_divulgacao"),
69
+ )
70
+ .unique()
71
+ )
72
+ if calendario["competencia"].is_duplicated().any():
73
+ raise ValueError("Datas conflitantes no calendário do IGP-M.")
74
+ return calendario.sort("competencia")
75
+
76
+
77
+ def calendario(ano: int, *, atualizar: bool = False) -> pl.DataFrame:
78
+ """Consulta o calendário de divulgação mensal do IGP-M na FGV IBRE.
79
+
80
+ Args:
81
+ ano: Ano das divulgações e competências.
82
+ atualizar: Ignora e renova o cache de 60 segundos desse ano.
83
+
84
+ Returns:
85
+ DataFrame ordenado por competência. Sem registros, retorna vazio com
86
+ o mesmo esquema.
87
+
88
+ Output Columns:
89
+ competencia (Date): Primeiro dia do mês da divulgação mensal do IGP-M.
90
+ data_divulgacao (Date): Data prevista pela FGV IBRE.
91
+
92
+ Notes:
93
+ Fonte: calendário de divulgação da FGV IBRE. Seleciona o IGP-M mensal
94
+ com seus componentes, excluindo prévias decendiais e outros índices.
95
+ A competência corresponde ao mês da divulgação mensal. A data prevista
96
+ não comprova publicação. Não cria janelas estimadas quando faltam dados.
97
+ Erros de rede e payload inválido são propagados.
98
+
99
+ Raises:
100
+ ValueError: Ano inválido, data inválida ou datas conflitantes.
101
+
102
+ Examples:
103
+ Seleciona as divulgações previstas a partir de hoje, sem incluir
104
+ prévias decendiais. O resultado depende do calendário da FGV IBRE
105
+ e não comprova que os índices já tenham sido publicados:
106
+
107
+ >>> hoje = yd.hoje()
108
+ >>> agenda = yd.igpm.calendario(hoje.year)
109
+ >>> proximas = agenda.filter(pl.col("data_divulgacao") >= hoje)
110
+ """
111
+ if type(ano) is not int or not dt.MINYEAR <= ano <= dt.MAXYEAR:
112
+ raise ValueError("ano deve ser um inteiro entre 1 e 9999.")
113
+ return _processar_calendario(
114
+ _parsear_calendario(_buscar_calendario(ano, _atualizar=atualizar))
115
+ ).filter(pl.col("competencia").dt.year() == ano)
@@ -0,0 +1,138 @@
1
+ """Números-índice do IGP-M da FGV disponibilizados pelo Ipeadata."""
2
+
3
+ import json
4
+ from decimal import Decimal
5
+
6
+ import polars as pl
7
+ import requests
8
+
9
+ from pyield import relogio
10
+ from pyield._internal.cache import ttl_cache
11
+ from pyield._internal.converters import converter_datas
12
+ from pyield._internal.retry import retry_padrao
13
+ from pyield._internal.types import DateLike, any_is_empty
14
+
15
+ _URL_INDICES = (
16
+ "https://www.ipeadata.gov.br/api/odata4/ValoresSerie(SERCODIGO='IGP12_IGPM12')"
17
+ )
18
+ _TIPO_INDICE = pl.Decimal(precision=18, scale=10)
19
+ _ESQUEMA = {"periodo": pl.Int64, "indice": _TIPO_INDICE}
20
+
21
+
22
+ @ttl_cache()
23
+ @retry_padrao
24
+ def _buscar_historico() -> bytes:
25
+ """Busca o JSON bruto da série de números-índice do IGP-M."""
26
+ resposta = requests.get(_URL_INDICES, timeout=30)
27
+ resposta.raise_for_status()
28
+ return resposta.content
29
+
30
+
31
+ def _parsear_historico(conteudo: bytes) -> list[dict[str, str | Decimal | None]]:
32
+ """Decodifica os números do JSON diretamente como Decimal."""
33
+ return json.loads(conteudo, parse_float=Decimal)["value"]
34
+
35
+
36
+ def _processar_historico(dados: list[dict[str, str | Decimal | None]]) -> pl.DataFrame:
37
+ """Normaliza as competências mensais e preserva a precisão dos índices."""
38
+ return (
39
+ pl.from_dicts(dados, schema={"VALDATA": pl.String, "VALVALOR": _TIPO_INDICE})
40
+ .select(
41
+ periodo=pl.col("VALDATA")
42
+ .str.slice(0, 10)
43
+ .str.to_date("%Y-%m-%d")
44
+ .dt.strftime("%Y%m")
45
+ .cast(pl.Int64),
46
+ indice=pl.col("VALVALOR"),
47
+ )
48
+ .drop_nulls("indice")
49
+ .sort("periodo")
50
+ )
51
+
52
+
53
+ def indice_serie(
54
+ inicio: DateLike | None = None,
55
+ fim: DateLike | None = None,
56
+ *,
57
+ ultimos: int | None = None,
58
+ ) -> pl.DataFrame:
59
+ """Obtém os números-índice mensais do IGP-M por intervalo ou últimos meses.
60
+
61
+ Fonte: FGV/Conjuntura Econômica, disponibilizada pelo Ipeadata na série
62
+ ``IGP12_IGPM12``, com base agosto de 1994 = 100.
63
+
64
+ Args:
65
+ inicio: Qualquer data no primeiro mês do intervalo.
66
+ fim: Qualquer data no último mês do intervalo. Se ``None``, usa a
67
+ data atual no Brasil.
68
+ ultimos: Número de meses mais recentes a recuperar. Se informado,
69
+ tem prioridade sobre ``inicio`` e ``fim``.
70
+
71
+ Returns:
72
+ DataFrame com os números-índice em ordem crescente de competência.
73
+ Retorna vazio, preservando o esquema, se não houver dados no intervalo.
74
+
75
+ Output Columns:
76
+ * periodo (Int64): competência mensal no formato YYYYMM.
77
+ * indice (Decimal(18, 10)): número-índice do IGP-M.
78
+
79
+ Notes:
80
+ Os números do JSON são lidos diretamente como Decimal, sem passagem
81
+ por float. As dez casas preservam também os valores anteriores a 1994,
82
+ que podem ser menores que 0.001. Valores ausentes são omitidos.
83
+
84
+ Raises:
85
+ ValueError: Se não informar ``inicio`` nem ``ultimos``, se ``ultimos``
86
+ for menor ou igual a zero ou se alguma data for inválida.
87
+
88
+ Examples:
89
+ >>> yd.igpm.indice_serie("01-06-2000", "31-07-2000").to_dicts()
90
+ [{'periodo': 200006, 'indice': Decimal('183.7450000000')},
91
+ {'periodo': 200007, 'indice': Decimal('186.6340000000')}]
92
+ """
93
+ if ultimos is not None:
94
+ if ultimos <= 0:
95
+ raise ValueError("O número de meses deve ser maior que 0.")
96
+ return _processar_historico(_parsear_historico(_buscar_historico())).tail(
97
+ ultimos
98
+ )
99
+ if inicio is None:
100
+ raise ValueError("Informe 'inicio' ou 'ultimos'.")
101
+ data_inicio = converter_datas(inicio)
102
+ data_fim = converter_datas(fim) if fim is not None else relogio.hoje()
103
+ if data_inicio is None or data_fim is None:
104
+ raise ValueError("As datas do intervalo devem ser informadas.")
105
+ periodo_inicio = data_inicio.year * 100 + data_inicio.month
106
+ periodo_fim = data_fim.year * 100 + data_fim.month
107
+ if periodo_inicio > periodo_fim:
108
+ return pl.DataFrame(schema=_ESQUEMA)
109
+ return _processar_historico(_parsear_historico(_buscar_historico())).filter(
110
+ pl.col("periodo").is_between(periodo_inicio, periodo_fim)
111
+ )
112
+
113
+
114
+ def indice(data: DateLike | None) -> Decimal:
115
+ """Obtém o número-índice do IGP-M para um mês específico.
116
+
117
+ Fonte: FGV/Conjuntura Econômica, disponibilizada pelo Ipeadata na série
118
+ ``IGP12_IGPM12``, com base agosto de 1994 = 100.
119
+
120
+ Args:
121
+ data: Qualquer data dentro do mês desejado, nos formatos da biblioteca.
122
+
123
+ Returns:
124
+ Decimal: Número-índice com dez casas decimais ou ``Decimal('NaN')``
125
+ se a data for nula, vazia ou o índice não estiver disponível.
126
+
127
+ Examples:
128
+ >>> yd.igpm.indice("01-06-2000")
129
+ Decimal('183.7450000000')
130
+ >>> yd.igpm.indice("01-06-1989")
131
+ Decimal('0.0000308619')
132
+ """
133
+ if data is None or any_is_empty(data):
134
+ return Decimal("NaN")
135
+ df = indice_serie(data, data)
136
+ if df.is_empty():
137
+ return Decimal("NaN")
138
+ return df.item(0, "indice")