pyield 0.51.2__tar.gz → 0.52.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 (70) hide show
  1. {pyield-0.51.2 → pyield-0.52.0}/PKG-INFO +83 -26
  2. {pyield-0.51.2 → pyield-0.52.0}/README.md +82 -25
  3. {pyield-0.51.2 → pyield-0.52.0}/pyield/__init__.py +2 -1
  4. {pyield-0.51.2 → pyield-0.52.0}/pyield/_internal/converters.py +3 -3
  5. {pyield-0.51.2 → pyield-0.52.0}/pyield/_internal/types.py +2 -1
  6. {pyield-0.51.2 → pyield-0.52.0}/pyield/bc/leiloes.py +36 -23
  7. {pyield-0.51.2 → pyield-0.52.0}/pyield/du/core.py +9 -9
  8. {pyield-0.51.2 → pyield-0.52.0}/pyield/futuro/di1.py +14 -59
  9. {pyield-0.51.2 → pyield-0.52.0}/pyield/futuro/historico.py +2 -2
  10. pyield-0.52.0/pyield/interpolador.py +495 -0
  11. {pyield-0.51.2 → pyield-0.52.0}/pyield/tpf/_titulos/ltn.py +85 -63
  12. {pyield-0.51.2 → pyield-0.52.0}/pyield/tpf/_titulos/ntnb.py +147 -61
  13. {pyield-0.51.2 → pyield-0.52.0}/pyield/tpf/_titulos/ntnb1.py +10 -9
  14. {pyield-0.51.2 → pyield-0.52.0}/pyield/tpf/_titulos/ntnbprinc.py +13 -9
  15. {pyield-0.51.2 → pyield-0.52.0}/pyield/tpf/_titulos/ntnc.py +89 -14
  16. {pyield-0.51.2 → pyield-0.52.0}/pyield/tpf/_titulos/ntnf.py +185 -73
  17. {pyield-0.51.2 → pyield-0.52.0}/pyield/tpf/leiloes.py +41 -32
  18. {pyield-0.51.2 → pyield-0.52.0}/pyield/tpf/pre.py +4 -4
  19. {pyield-0.51.2 → pyield-0.52.0}/pyield/tpf/utils.py +7 -0
  20. {pyield-0.51.2 → pyield-0.52.0}/pyproject.toml +1 -1
  21. pyield-0.51.2/pyield/interpolador.py +0 -263
  22. {pyield-0.51.2 → pyield-0.52.0}/LICENSE +0 -0
  23. {pyield-0.51.2 → pyield-0.52.0}/pyield/_internal/__init__.py +0 -0
  24. {pyield-0.51.2 → pyield-0.52.0}/pyield/_internal/br_numbers.py +0 -0
  25. {pyield-0.51.2 → pyield-0.52.0}/pyield/_internal/cache.py +0 -0
  26. {pyield-0.51.2 → pyield-0.52.0}/pyield/_internal/data_cache.py +0 -0
  27. {pyield-0.51.2 → pyield-0.52.0}/pyield/_internal/retry.py +0 -0
  28. {pyield-0.51.2 → pyield-0.52.0}/pyield/anbima/__init__.py +0 -0
  29. {pyield-0.51.2 → pyield-0.52.0}/pyield/anbima/imaq.py +0 -0
  30. {pyield-0.51.2 → pyield-0.52.0}/pyield/anbima/mercado_secundario.py +0 -0
  31. {pyield-0.51.2 → pyield-0.52.0}/pyield/b3/__init__.py +0 -0
  32. {pyield-0.51.2 → pyield-0.52.0}/pyield/b3/_contratos.py +0 -0
  33. {pyield-0.51.2 → pyield-0.52.0}/pyield/b3/_validar_pregao.py +0 -0
  34. {pyield-0.51.2 → pyield-0.52.0}/pyield/b3/boletim.py +0 -0
  35. {pyield-0.51.2 → pyield-0.52.0}/pyield/b3/derivativos_intradia.py +0 -0
  36. {pyield-0.51.2 → pyield-0.52.0}/pyield/b3/di_over.py +0 -0
  37. {pyield-0.51.2 → pyield-0.52.0}/pyield/bc/__init__.py +0 -0
  38. {pyield-0.51.2 → pyield-0.52.0}/pyield/bc/_olinda.py +0 -0
  39. {pyield-0.51.2 → pyield-0.52.0}/pyield/bc/lft.py +0 -0
  40. {pyield-0.51.2 → pyield-0.52.0}/pyield/bc/sgs.py +0 -0
  41. {pyield-0.51.2 → pyield-0.52.0}/pyield/bc/tpf_intradia.py +0 -0
  42. {pyield-0.51.2 → pyield-0.52.0}/pyield/bc/tpf_mensal.py +0 -0
  43. {pyield-0.51.2 → pyield-0.52.0}/pyield/du/__init__.py +0 -0
  44. {pyield-0.51.2 → pyield-0.52.0}/pyield/du/feriados/__init__.py +0 -0
  45. {pyield-0.51.2 → pyield-0.52.0}/pyield/du/feriados/feriados_antigos_br.txt +0 -0
  46. {pyield-0.51.2 → pyield-0.52.0}/pyield/du/feriados/feriados_br.py +0 -0
  47. {pyield-0.51.2 → pyield-0.52.0}/pyield/du/feriados/feriados_novos_br.txt +0 -0
  48. {pyield-0.51.2 → pyield-0.52.0}/pyield/futuro/__init__.py +0 -0
  49. {pyield-0.51.2 → pyield-0.52.0}/pyield/futuro/contratos.py +0 -0
  50. {pyield-0.51.2 → pyield-0.52.0}/pyield/futuro/intradia.py +0 -0
  51. {pyield-0.51.2 → pyield-0.52.0}/pyield/fwd.py +0 -0
  52. {pyield-0.51.2 → pyield-0.52.0}/pyield/ipca/__init__.py +0 -0
  53. {pyield-0.51.2 → pyield-0.52.0}/pyield/ipca/historico.py +0 -0
  54. {pyield-0.51.2 → pyield-0.52.0}/pyield/ipca/projetado.py +0 -0
  55. {pyield-0.51.2 → pyield-0.52.0}/pyield/py.typed +0 -0
  56. {pyield-0.51.2 → pyield-0.52.0}/pyield/relogio.py +0 -0
  57. {pyield-0.51.2 → pyield-0.52.0}/pyield/selic/__init__.py +0 -0
  58. {pyield-0.51.2 → pyield-0.52.0}/pyield/selic/compromissada.py +0 -0
  59. {pyield-0.51.2 → pyield-0.52.0}/pyield/selic/copom.py +0 -0
  60. {pyield-0.51.2 → pyield-0.52.0}/pyield/selic/cpm.py +0 -0
  61. {pyield-0.51.2 → pyield-0.52.0}/pyield/selic/probabilities.py +0 -0
  62. {pyield-0.51.2 → pyield-0.52.0}/pyield/tpf/__init__.py +0 -0
  63. {pyield-0.51.2 → pyield-0.52.0}/pyield/tpf/_titulos/__init__.py +0 -0
  64. {pyield-0.51.2 → pyield-0.52.0}/pyield/tpf/_titulos/lft.py +0 -0
  65. {pyield-0.51.2 → pyield-0.52.0}/pyield/tpf/benchmark.py +0 -0
  66. {pyield-0.51.2 → pyield-0.52.0}/pyield/tpf/rmd/__init__.py +0 -0
  67. {pyield-0.51.2 → pyield-0.52.0}/pyield/tpf/rmd/_aba_1_3.py +0 -0
  68. {pyield-0.51.2 → pyield-0.52.0}/pyield/tpf/rmd/_aba_2_1.py +0 -0
  69. {pyield-0.51.2 → pyield-0.52.0}/pyield/tpf/rmd/_common.py +0 -0
  70. {pyield-0.51.2 → pyield-0.52.0}/pyield/tpf/rmd/_download.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pyield
3
- Version: 0.51.2
3
+ Version: 0.52.0
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
@@ -86,10 +86,9 @@ df = yd.futuro.historico("31-05-2024", "DI1")
86
86
 
87
87
  # Interpolação de taxas (flat forward, convenção 252 dias úteis/ano)
88
88
  interp = yd.Interpolador(df["dias_uteis"], df["taxa_ajuste"], metodo="flat_forward")
89
- interp(45) # -> 0.04833...
90
- interp([30, 60]) # -> Series do Polars com taxas interpoladas
89
+ interp(45) # -> 0.04833...
91
90
 
92
- # Precificação de títulos públicos
91
+ # Preçificar títulos públicos
93
92
  yd.ntnb.cotacao("31-05-2024", "15-05-2035", 0.061490) # -> 99.3651
94
93
 
95
94
  # Indicadores do BCB
@@ -108,19 +107,20 @@ documentação.
108
107
  | Componente | Tipo | Finalidade | Funções públicas |
109
108
  |---|---|---|---|
110
109
  | `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` |
111
- | `yd.Interpolador` | classe | Interpolação de curvas | `interpolar`, `__call__`, `linear`, `flat_forward` |
110
+ | `yd.Interpolador` | classe | Interpolação escalar e em pipelines Polars | `interpolar`, `interpolar_expr`, `linear`, `flat_forward` |
111
+ | `yd.interpolar(...)` | função | Interpolação vetorizada flat-forward, curva única ou multi-curva | |
112
112
  | `yd.forward(...)` | função | Taxa a termo entre dois vértices | |
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
116
  | `yd.tpf` | módulo | Títulos públicos federais | `taxas`, `vencimentos`, `estoque`, `leiloes`, `benchmarks`, `curva_pre`, `premio_pre`, `rmd`, `secundario_mensal`, `secundario_intradia` |
117
117
  | `yd.lft` | módulo | LFT | `dados`, `vencimentos`, `cotacao`, `taxa`, `vna` |
118
- | `yd.ltn` | módulo | LTN | `dados`, `vencimentos`, `pu`, `taxa`, `dv01`, `premio`, `rentabilidade`, `taxas_forward` |
119
- | `yd.ntnb` | módulo | NTN-B | `dados`, `vencimentos`, `datas_pagamento`, `fluxos_caixa`, `cotacao`, `pu`, `taxa`, `taxas_zero`, `duration`, `dv01`, `implicitas`, `curva` |
118
+ | `yd.ltn` | módulo | LTN | `dados`, `vencimentos`, `pu`, `taxa`, `duration_expr`, `dv01`, `dv01_expr`, `rentabilidade`, `rentabilidade_expr`, `taxas_forward` |
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` |
120
120
  | `yd.ntnb1` | módulo | NTN-B1 | `datas_pagamento`, `fluxos_caixa`, `cotacao`, `pu`, `duration`, `dv01` |
121
121
  | `yd.ntnbprinc` | módulo | NTN-B Principal | `pu`, `dv01` |
122
- | `yd.ntnc` | módulo | NTN-C | `dados`, `datas_pagamento`, `fluxos_caixa`, `cotacao`, `pu`, `taxa`, `duration`, `dv01` |
123
- | `yd.ntnf` | módulo | NTN-F | `dados`, `vencimentos`, `datas_pagamento`, `fluxos_caixa`, `pu`, `taxa`, `taxas_zero`, `premio`, `premio_limpo`, `rentabilidade`, `duration`, `dv01` |
122
+ | `yd.ntnc` | módulo | NTN-C | `dados`, `datas_pagamento`, `fluxos_caixa`, `cotacao`, `pu`, `taxa`, `duration`, `duration_expr`, `dv01`, `dv01_expr` |
123
+ | `yd.ntnf` | módulo | NTN-F | `dados`, `vencimentos`, `datas_pagamento`, `fluxos_caixa`, `pu`, `taxa`, `taxas_zero`, `premio`, `premio_limpo`, `rentabilidade`, `rentabilidade_expr`, `duration`, `duration_expr`, `dv01`, `dv01_expr` |
124
124
  | `yd.selic` | módulo | Selic, COPOM e política monetária | `over`, `over_serie`, `meta`, `meta_serie`, `compromissadas`, `copom`, `cpm`, `probabilities` |
125
125
  | `yd.ipca` | módulo | IPCA histórico e projetado | `indice`, `indices`, `indices_ultimos`, `taxa`, `taxas`, `taxas_ultimas`, `taxa_projetada` |
126
126
  | `yd.ptax(data)` | função | PTAX para uma data | |
@@ -178,14 +178,37 @@ interp(45) # -> 0.04833...
178
178
  linear = Interpolador(dias_uteis, taxas, metodo="linear")
179
179
  linear(45) # -> 0.0475
180
180
 
181
- # Vetorizado
182
- interp([15, 45, 75]) # -> pl.Series com 3 taxas
183
-
184
- # Extrapolação (desabilitada por padrão, retorna NaN)
181
+ # Extrapolação na ponta longa: desabilitada por padrão (NaN). A ponta
182
+ # curta sempre retorna a primeira taxa conhecida.
185
183
  interp(100) # -> nan
186
184
  Interpolador(dias_uteis, taxas, metodo="flat_forward", extrapolar=True)(100) # -> 0.055
187
185
  ```
188
186
 
187
+ Para interpolar uma coluna inteira dentro de um pipeline Polars, use
188
+ `interpolar_expr`:
189
+
190
+ ```python
191
+ import polars as pl
192
+
193
+ df = pl.DataFrame({"du": [15, 45, 75]})
194
+ df.with_columns(taxa=interp.interpolar_expr("du"))
195
+ ```
196
+
197
+ Quando os pontos alvo e a curva vêm de DataFrames diferentes (inclusive com
198
+ múltiplas datas de referência), use a função top-level `yd.interpolar`:
199
+
200
+ ```python
201
+ import pyield as yd
202
+
203
+ taxas = yd.interpolar(
204
+ dus_alvo=df_alvo["dias_uteis"],
205
+ dus_curva=df_curva["dias_uteis"],
206
+ taxas_curva=df_curva["taxa"],
207
+ datas_alvo=df_alvo["data_referencia"], # opcional (multi-curva)
208
+ datas_curva=df_curva["data_referencia"], # opcional (multi-curva)
209
+ )
210
+ ```
211
+
189
212
  ### Taxas a Termo (`forward`, `forwards`)
190
213
 
191
214
  Calcula taxas a termo a partir de curvas spot:
@@ -290,28 +313,62 @@ yd.ptax("25-12-2025") # -> nan
290
313
 
291
314
  Documentação completa: [crdcj.github.io/PYield](https://crdcj.github.io/PYield/)
292
315
 
293
- ## Quebra de API (v0.51.0)
316
+ ## Quebra de API (v0.52.0)
317
+
318
+ Resumo das quebras desta versão:
319
+
320
+ - `Interpolador(...)` e `Interpolador.interpolar(...)` agora aceitam **apenas
321
+ inteiro escalar**. Chamadas com lista/`pl.Series` (`interp([30, 60])`),
322
+ antes suportadas via sobrecarga vetorial, agora levantam `TypeError`.
323
+ Substitua por `Interpolador.interpolar_expr` em pipelines Polars ou pela
324
+ função top-level `yd.interpolar(...)` (curva única ou multi-curva).
325
+ - As funções `dv01(...)` dos títulos públicos agora recebem `pu` como argumento
326
+ explícito. Chamadas antigas que passavam apenas data, vencimento e taxa, ou
327
+ que passavam VNA no caso da NTN-B, precisam ser atualizadas.
328
+ - `ntnf.taxas_zero(...)` mudou os nomes dos parâmetros de curva para o padrão
329
+ `vencimentos_*` / `taxas_*`. Chamadas por posição continuam com a mesma
330
+ ordem; chamadas por keyword precisam usar os novos nomes.
294
331
 
295
- ### Remoção de `dv01_usd`
332
+ ### `Interpolador(...)` não aceita mais lista/Series
296
333
 
297
- A coluna `dv01_usd` foi removida da saída de `dados()` nos módulos `ntnb`,
298
- `ltn`, `ntnc` e `ntnf`. Para converter o DV01 para USD, divida pela PTAX:
334
+ O ``__call__`` e o método ``interpolar`` da classe agora são estritamente
335
+ escalares. O caminho vetorial foi dividido em duas APIs com semântica clara:
299
336
 
300
337
  ```python
301
- df = yd.ntnb.dados("06-06-2026")
302
- dv01_usd = df["dv01"] / yd.ptax("06-06-2026")
338
+ # Antes (v0.51.x)
339
+ interp = yd.Interpolador(dus, taxas, metodo="flat_forward")
340
+ interp([15, 45, 75]) # pl.Series
341
+
342
+ # Agora, dentro de um pipeline Polars
343
+ df.with_columns(taxa=interp.interpolar_expr("du"))
344
+
345
+ # Agora, ad-hoc (curva única ou multi-curva)
346
+ yd.interpolar(
347
+ dus_alvo=pl.Series([15, 45, 75]),
348
+ dus_curva=pl.Series(dus),
349
+ taxas_curva=pl.Series(taxas),
350
+ )
303
351
  ```
304
352
 
305
- ### `ntnb.inflacao_implicita()` `ntnb.implicitas()`
353
+ ### `dv01(...)` agora recebe PU
354
+
355
+ As funções `dv01(...)` dos títulos públicos recebem o PU usado como base para o
356
+ cálculo, não mais o VNA ou apenas a taxa. Se necessário, calcule o PU antes:
357
+
358
+ ```python
359
+ pu = yd.ntnb.pu(vna, yd.ntnb.cotacao(data, vencimento, taxa))
360
+ dv01 = yd.ntnb.dv01(data, vencimento, taxa, pu)
361
+ ```
306
362
 
307
- A função foi renomeada. As colunas de saída também mudaram:
308
- - `taxa_tir` → `taxa_tir_real`
309
- - `taxa_zero` → `taxa_zero_real`
363
+ ### `ntnf.taxas_zero(...)` usa nomes por conceito
310
364
 
311
- ### Remoção de `ntnb.forward()`
365
+ Os parâmetros de curva da NTN-F foram renomeados para manter o padrão
366
+ `vencimentos_*` / `taxas_*`:
312
367
 
313
- A função `ntnb.forward(data, usar_taxa_zero=True)` foi removida. Os dados que
314
- ela retornava estão disponíveis via `ntnb.dados(data)`.
368
+ - `ltn_vencimentos` `vencimentos_ltn`
369
+ - `ltn_taxas` `taxas_ltn`
370
+ - `ntnf_vencimentos` → `vencimentos_ntnf`
371
+ - `ntnf_taxas` → `taxas_ntnf`
315
372
 
316
373
  ## Testes
317
374
 
@@ -42,10 +42,9 @@ df = yd.futuro.historico("31-05-2024", "DI1")
42
42
 
43
43
  # Interpolação de taxas (flat forward, convenção 252 dias úteis/ano)
44
44
  interp = yd.Interpolador(df["dias_uteis"], df["taxa_ajuste"], metodo="flat_forward")
45
- interp(45) # -> 0.04833...
46
- interp([30, 60]) # -> Series do Polars com taxas interpoladas
45
+ interp(45) # -> 0.04833...
47
46
 
48
- # Precificação de títulos públicos
47
+ # Preçificar títulos públicos
49
48
  yd.ntnb.cotacao("31-05-2024", "15-05-2035", 0.061490) # -> 99.3651
50
49
 
51
50
  # Indicadores do BCB
@@ -64,19 +63,20 @@ documentação.
64
63
  | Componente | Tipo | Finalidade | Funções públicas |
65
64
  |---|---|---|---|
66
65
  | `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` |
67
- | `yd.Interpolador` | classe | Interpolação de curvas | `interpolar`, `__call__`, `linear`, `flat_forward` |
66
+ | `yd.Interpolador` | classe | Interpolação escalar e em pipelines Polars | `interpolar`, `interpolar_expr`, `linear`, `flat_forward` |
67
+ | `yd.interpolar(...)` | função | Interpolação vetorizada flat-forward, curva única ou multi-curva | |
68
68
  | `yd.forward(...)` | função | Taxa a termo entre dois vértices | |
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
72
  | `yd.tpf` | módulo | Títulos públicos federais | `taxas`, `vencimentos`, `estoque`, `leiloes`, `benchmarks`, `curva_pre`, `premio_pre`, `rmd`, `secundario_mensal`, `secundario_intradia` |
73
73
  | `yd.lft` | módulo | LFT | `dados`, `vencimentos`, `cotacao`, `taxa`, `vna` |
74
- | `yd.ltn` | módulo | LTN | `dados`, `vencimentos`, `pu`, `taxa`, `dv01`, `premio`, `rentabilidade`, `taxas_forward` |
75
- | `yd.ntnb` | módulo | NTN-B | `dados`, `vencimentos`, `datas_pagamento`, `fluxos_caixa`, `cotacao`, `pu`, `taxa`, `taxas_zero`, `duration`, `dv01`, `implicitas`, `curva` |
74
+ | `yd.ltn` | módulo | LTN | `dados`, `vencimentos`, `pu`, `taxa`, `duration_expr`, `dv01`, `dv01_expr`, `rentabilidade`, `rentabilidade_expr`, `taxas_forward` |
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` |
76
76
  | `yd.ntnb1` | módulo | NTN-B1 | `datas_pagamento`, `fluxos_caixa`, `cotacao`, `pu`, `duration`, `dv01` |
77
77
  | `yd.ntnbprinc` | módulo | NTN-B Principal | `pu`, `dv01` |
78
- | `yd.ntnc` | módulo | NTN-C | `dados`, `datas_pagamento`, `fluxos_caixa`, `cotacao`, `pu`, `taxa`, `duration`, `dv01` |
79
- | `yd.ntnf` | módulo | NTN-F | `dados`, `vencimentos`, `datas_pagamento`, `fluxos_caixa`, `pu`, `taxa`, `taxas_zero`, `premio`, `premio_limpo`, `rentabilidade`, `duration`, `dv01` |
78
+ | `yd.ntnc` | módulo | NTN-C | `dados`, `datas_pagamento`, `fluxos_caixa`, `cotacao`, `pu`, `taxa`, `duration`, `duration_expr`, `dv01`, `dv01_expr` |
79
+ | `yd.ntnf` | módulo | NTN-F | `dados`, `vencimentos`, `datas_pagamento`, `fluxos_caixa`, `pu`, `taxa`, `taxas_zero`, `premio`, `premio_limpo`, `rentabilidade`, `rentabilidade_expr`, `duration`, `duration_expr`, `dv01`, `dv01_expr` |
80
80
  | `yd.selic` | módulo | Selic, COPOM e política monetária | `over`, `over_serie`, `meta`, `meta_serie`, `compromissadas`, `copom`, `cpm`, `probabilities` |
81
81
  | `yd.ipca` | módulo | IPCA histórico e projetado | `indice`, `indices`, `indices_ultimos`, `taxa`, `taxas`, `taxas_ultimas`, `taxa_projetada` |
82
82
  | `yd.ptax(data)` | função | PTAX para uma data | |
@@ -134,14 +134,37 @@ interp(45) # -> 0.04833...
134
134
  linear = Interpolador(dias_uteis, taxas, metodo="linear")
135
135
  linear(45) # -> 0.0475
136
136
 
137
- # Vetorizado
138
- interp([15, 45, 75]) # -> pl.Series com 3 taxas
139
-
140
- # Extrapolação (desabilitada por padrão, retorna NaN)
137
+ # Extrapolação na ponta longa: desabilitada por padrão (NaN). A ponta
138
+ # curta sempre retorna a primeira taxa conhecida.
141
139
  interp(100) # -> nan
142
140
  Interpolador(dias_uteis, taxas, metodo="flat_forward", extrapolar=True)(100) # -> 0.055
143
141
  ```
144
142
 
143
+ Para interpolar uma coluna inteira dentro de um pipeline Polars, use
144
+ `interpolar_expr`:
145
+
146
+ ```python
147
+ import polars as pl
148
+
149
+ df = pl.DataFrame({"du": [15, 45, 75]})
150
+ df.with_columns(taxa=interp.interpolar_expr("du"))
151
+ ```
152
+
153
+ Quando os pontos alvo e a curva vêm de DataFrames diferentes (inclusive com
154
+ múltiplas datas de referência), use a função top-level `yd.interpolar`:
155
+
156
+ ```python
157
+ import pyield as yd
158
+
159
+ taxas = yd.interpolar(
160
+ dus_alvo=df_alvo["dias_uteis"],
161
+ dus_curva=df_curva["dias_uteis"],
162
+ taxas_curva=df_curva["taxa"],
163
+ datas_alvo=df_alvo["data_referencia"], # opcional (multi-curva)
164
+ datas_curva=df_curva["data_referencia"], # opcional (multi-curva)
165
+ )
166
+ ```
167
+
145
168
  ### Taxas a Termo (`forward`, `forwards`)
146
169
 
147
170
  Calcula taxas a termo a partir de curvas spot:
@@ -246,28 +269,62 @@ yd.ptax("25-12-2025") # -> nan
246
269
 
247
270
  Documentação completa: [crdcj.github.io/PYield](https://crdcj.github.io/PYield/)
248
271
 
249
- ## Quebra de API (v0.51.0)
272
+ ## Quebra de API (v0.52.0)
273
+
274
+ Resumo das quebras desta versão:
275
+
276
+ - `Interpolador(...)` e `Interpolador.interpolar(...)` agora aceitam **apenas
277
+ inteiro escalar**. Chamadas com lista/`pl.Series` (`interp([30, 60])`),
278
+ antes suportadas via sobrecarga vetorial, agora levantam `TypeError`.
279
+ Substitua por `Interpolador.interpolar_expr` em pipelines Polars ou pela
280
+ função top-level `yd.interpolar(...)` (curva única ou multi-curva).
281
+ - As funções `dv01(...)` dos títulos públicos agora recebem `pu` como argumento
282
+ explícito. Chamadas antigas que passavam apenas data, vencimento e taxa, ou
283
+ que passavam VNA no caso da NTN-B, precisam ser atualizadas.
284
+ - `ntnf.taxas_zero(...)` mudou os nomes dos parâmetros de curva para o padrão
285
+ `vencimentos_*` / `taxas_*`. Chamadas por posição continuam com a mesma
286
+ ordem; chamadas por keyword precisam usar os novos nomes.
250
287
 
251
- ### Remoção de `dv01_usd`
288
+ ### `Interpolador(...)` não aceita mais lista/Series
252
289
 
253
- A coluna `dv01_usd` foi removida da saída de `dados()` nos módulos `ntnb`,
254
- `ltn`, `ntnc` e `ntnf`. Para converter o DV01 para USD, divida pela PTAX:
290
+ O ``__call__`` e o método ``interpolar`` da classe agora são estritamente
291
+ escalares. O caminho vetorial foi dividido em duas APIs com semântica clara:
255
292
 
256
293
  ```python
257
- df = yd.ntnb.dados("06-06-2026")
258
- dv01_usd = df["dv01"] / yd.ptax("06-06-2026")
294
+ # Antes (v0.51.x)
295
+ interp = yd.Interpolador(dus, taxas, metodo="flat_forward")
296
+ interp([15, 45, 75]) # pl.Series
297
+
298
+ # Agora, dentro de um pipeline Polars
299
+ df.with_columns(taxa=interp.interpolar_expr("du"))
300
+
301
+ # Agora, ad-hoc (curva única ou multi-curva)
302
+ yd.interpolar(
303
+ dus_alvo=pl.Series([15, 45, 75]),
304
+ dus_curva=pl.Series(dus),
305
+ taxas_curva=pl.Series(taxas),
306
+ )
259
307
  ```
260
308
 
261
- ### `ntnb.inflacao_implicita()` `ntnb.implicitas()`
309
+ ### `dv01(...)` agora recebe PU
310
+
311
+ As funções `dv01(...)` dos títulos públicos recebem o PU usado como base para o
312
+ cálculo, não mais o VNA ou apenas a taxa. Se necessário, calcule o PU antes:
313
+
314
+ ```python
315
+ pu = yd.ntnb.pu(vna, yd.ntnb.cotacao(data, vencimento, taxa))
316
+ dv01 = yd.ntnb.dv01(data, vencimento, taxa, pu)
317
+ ```
262
318
 
263
- A função foi renomeada. As colunas de saída também mudaram:
264
- - `taxa_tir` → `taxa_tir_real`
265
- - `taxa_zero` → `taxa_zero_real`
319
+ ### `ntnf.taxas_zero(...)` usa nomes por conceito
266
320
 
267
- ### Remoção de `ntnb.forward()`
321
+ Os parâmetros de curva da NTN-F foram renomeados para manter o padrão
322
+ `vencimentos_*` / `taxas_*`:
268
323
 
269
- A função `ntnb.forward(data, usar_taxa_zero=True)` foi removida. Os dados que
270
- ela retornava estão disponíveis via `ntnb.dados(data)`.
324
+ - `ltn_vencimentos` `vencimentos_ltn`
325
+ - `ltn_taxas` `taxas_ltn`
326
+ - `ntnf_vencimentos` → `vencimentos_ntnf`
327
+ - `ntnf_taxas` → `taxas_ntnf`
271
328
 
272
329
  ## Testes
273
330
 
@@ -13,7 +13,7 @@ from pyield.futuro import di1
13
13
  from pyield import selic, tpf
14
14
  from pyield.bc.sgs import ptax, ptax_serie
15
15
  from pyield.fwd import forward, forwards, forwards_expr
16
- from pyield.interpolador import Interpolador
16
+ from pyield.interpolador import Interpolador, interpolar
17
17
  from pyield.relogio import agora, hoje
18
18
  from pyield.tpf import lft, ltn, ntnb, ntnb1, ntnbprinc, ntnc, ntnf
19
19
 
@@ -35,6 +35,7 @@ __all__ = [
35
35
  "futuro",
36
36
  "hoje",
37
37
  "Interpolador",
38
+ "interpolar",
38
39
  "ipca",
39
40
  "lft",
40
41
  "ltn",
@@ -4,7 +4,7 @@ from typing import overload
4
4
  import polars as pl
5
5
 
6
6
  from pyield._internal import types
7
- from pyield._internal.types import ArrayLike, DateLike
7
+ from pyield._internal.types import DateLike, DatesLike
8
8
 
9
9
 
10
10
  def data_referencia_valida(date: dt.date | None) -> bool:
@@ -68,11 +68,11 @@ def converter_datas(dates: None) -> None: ...
68
68
  @overload
69
69
  def converter_datas(dates: DateLike) -> dt.date: ...
70
70
  @overload
71
- def converter_datas(dates: ArrayLike) -> pl.Series: ...
71
+ def converter_datas(dates: DatesLike) -> pl.Series: ...
72
72
 
73
73
 
74
74
  def converter_datas(
75
- dates: DateLike | ArrayLike | None,
75
+ dates: DateLike | DatesLike | None,
76
76
  ) -> dt.date | pl.Series | None:
77
77
  """Converte diferentes tipos de entrada para ``datetime.date`` ou ``pl.Series``.
78
78
 
@@ -1,12 +1,13 @@
1
1
  import datetime as dt
2
2
  import math
3
- from collections.abc import Sized
3
+ from collections.abc import Sequence, Sized
4
4
  from typing import Any, TypeAlias, TypeGuard
5
5
 
6
6
  import polars as pl
7
7
 
8
8
  DateLike: TypeAlias = str | dt.datetime | dt.date
9
9
  ArrayLike: TypeAlias = list[Any] | tuple[Any, ...] | pl.Series
10
+ DatesLike: TypeAlias = Sequence[DateLike] | pl.Series
10
11
 
11
12
 
12
13
  def _is_empty(arg: Any) -> bool:
@@ -19,8 +19,7 @@ from pyield._internal.br_numbers import float_br, taxa_br
19
19
  from pyield._internal.types import DateLike
20
20
  from pyield.bc._olinda import buscar_csv, montar_url, parsear_csv
21
21
  from pyield.bc.sgs import ptax_serie
22
- from pyield.tpf._titulos.ntnb import duration as duration_b
23
- from pyield.tpf._titulos.ntnf import duration as duration_f
22
+ from pyield.tpf._titulos import ltn, ntnb, ntnf
24
23
 
25
24
  registro = logging.getLogger(__name__)
26
25
 
@@ -90,22 +89,26 @@ def _processar_df(df: pl.DataFrame) -> pl.DataFrame:
90
89
  """Filtra, converte tipos e calcula colunas derivadas."""
91
90
  data_mudanca = dt.date(2024, 6, 11)
92
91
 
93
- def _duracao_por_linha(linha: dict) -> float:
94
- tipo = linha["titulo"]
95
- if tipo == "LTN":
96
- return linha["dias_uteis"] / 252
97
- if tipo == "NTN-F":
98
- return duration_f(
99
- linha["data_liquidacao"], linha["data_vencimento"], linha["taxa_media"]
92
+ expr_dv01_unitario = (
93
+ pl.when(pl.col("titulo") == "LTN")
94
+ .then(
95
+ ltn.dv01_expr(
96
+ "data_liquidacao", "data_vencimento", "taxa_media", "pu_medio"
100
97
  )
101
- if tipo == "NTN-B":
102
- return duration_b(
103
- linha["data_liquidacao"], linha["data_vencimento"], linha["taxa_media"]
98
+ )
99
+ .when(pl.col("titulo") == "NTN-F")
100
+ .then(
101
+ ntnf.dv01_expr(
102
+ "data_liquidacao", "data_vencimento", "taxa_media", "pu_medio"
104
103
  )
105
- return 0.0
106
-
107
- expr_dv01_unitario = (
108
- 0.0001 * pl.col("pu_medio") * pl.col("duration") / (1 + pl.col("taxa_media"))
104
+ )
105
+ .when(pl.col("titulo") == "NTN-B")
106
+ .then(
107
+ ntnb.dv01_expr(
108
+ "data_liquidacao", "data_vencimento", "taxa_media", "pu_medio"
109
+ )
110
+ )
111
+ .otherwise(0.0)
109
112
  )
110
113
 
111
114
  colunas_preco_taxa = ["taxa_media", "taxa_corte", "pu_medio", "pu_corte"]
@@ -177,13 +180,23 @@ def _processar_df(df: pl.DataFrame) -> pl.DataFrame:
177
180
  .name.keep()
178
181
  )
179
182
  .with_columns(
180
- duration=pl.struct(
181
- "titulo",
182
- "data_liquidacao",
183
- "data_vencimento",
184
- "taxa_media",
185
- "dias_uteis",
186
- ).map_elements(_duracao_por_linha, return_dtype=pl.Float64)
183
+ duration=(
184
+ pl.when(pl.col("titulo") == "LTN")
185
+ .then(ltn.duration_expr("data_liquidacao", "data_vencimento"))
186
+ .when(pl.col("titulo") == "NTN-F")
187
+ .then(
188
+ ntnf.duration_expr(
189
+ "data_liquidacao", "data_vencimento", "taxa_media"
190
+ )
191
+ )
192
+ .when(pl.col("titulo") == "NTN-B")
193
+ .then(
194
+ ntnb.duration_expr(
195
+ "data_liquidacao", "data_vencimento", "taxa_media"
196
+ )
197
+ )
198
+ .otherwise(0.0)
199
+ )
187
200
  )
188
201
  .with_columns(
189
202
  dv01_total=expr_dv01_unitario * pl.col("quantidade_aceita_total"),
@@ -6,7 +6,7 @@ import polars as pl
6
6
  import pyield._internal.converters as cv
7
7
  import pyield._internal.types as tp
8
8
  from pyield import relogio
9
- from pyield._internal.types import ArrayLike, DateLike
9
+ from pyield._internal.types import ArrayLike, DateLike, DatesLike
10
10
  from pyield.du.feriados.feriados_br import FeriadosBrasil
11
11
 
12
12
  LIMITE_DIA_UTIL = 6
@@ -87,9 +87,9 @@ def contar_expr(
87
87
 
88
88
 
89
89
  @overload
90
- def contar(inicio: ArrayLike, fim: ArrayLike | DateLike | None) -> pl.Series: ...
90
+ def contar(inicio: DatesLike, fim: DatesLike | DateLike | None) -> pl.Series: ...
91
91
  @overload
92
- def contar(inicio: DateLike | None, fim: ArrayLike) -> pl.Series: ...
92
+ def contar(inicio: DateLike | None, fim: DatesLike) -> pl.Series: ...
93
93
  @overload
94
94
  def contar(inicio: DateLike, fim: DateLike) -> int: ...
95
95
  @overload
@@ -99,8 +99,8 @@ def contar(inicio: None, fim: DateLike | None) -> None: ...
99
99
 
100
100
 
101
101
  def contar(
102
- inicio: None | DateLike | ArrayLike,
103
- fim: None | DateLike | ArrayLike,
102
+ inicio: None | DateLike | DatesLike,
103
+ fim: None | DateLike | DatesLike,
104
104
  ) -> None | int | pl.Series:
105
105
  """Conta dias úteis entre ``inicio`` (inclusivo) e ``fim`` (exclusivo).
106
106
 
@@ -286,7 +286,7 @@ def deslocar_expr(
286
286
 
287
287
  @overload
288
288
  def deslocar(
289
- datas: ArrayLike,
289
+ datas: DatesLike,
290
290
  deslocamento: ArrayLike | int | None,
291
291
  rolagem: Literal["forward", "backward"] = ...,
292
292
  ) -> pl.Series: ...
@@ -317,7 +317,7 @@ def deslocar(
317
317
 
318
318
 
319
319
  def deslocar(
320
- datas: DateLike | ArrayLike | None,
320
+ datas: DateLike | DatesLike | None,
321
321
  deslocamento: int | ArrayLike | None,
322
322
  rolagem: Literal["forward", "backward"] = "forward",
323
323
  ) -> dt.date | pl.Series | None:
@@ -604,10 +604,10 @@ def eh_dia_util(datas: None) -> None: ...
604
604
  @overload
605
605
  def eh_dia_util(datas: DateLike) -> bool: ...
606
606
  @overload
607
- def eh_dia_util(datas: ArrayLike) -> pl.Series: ...
607
+ def eh_dia_util(datas: DatesLike) -> pl.Series: ...
608
608
 
609
609
 
610
- def eh_dia_util(datas: None | DateLike | ArrayLike) -> None | bool | pl.Series:
610
+ def eh_dia_util(datas: None | DateLike | DatesLike) -> None | bool | pl.Series:
611
611
  """Determina se data(s) são dias úteis brasileiros.
612
612
 
613
613
  REGIME DE FERIADOS POR LINHA: Para CADA data de entrada, a lista de feriados
@@ -12,10 +12,10 @@ import polars as pl
12
12
  import pyield._internal.converters as cv
13
13
  from pyield import du
14
14
  from pyield._internal.data_cache import obter_dataset_cacheado
15
- from pyield._internal.types import ArrayLike, DateLike, any_is_array_like, any_is_empty
15
+ from pyield._internal.types import DateLike, DatesLike, any_is_array_like, any_is_empty
16
16
  from pyield.futuro.historico import buscar_historico_cacheado
17
17
  from pyield.futuro.historico import datas_disponiveis as _datas_futuro
18
- from pyield.interpolador import Interpolador
18
+ from pyield.interpolador import interpolar
19
19
 
20
20
  TipoTaxaDI1 = Literal["ajuste", "fechamento"]
21
21
 
@@ -26,7 +26,7 @@ _COLUNAS_TIPO_TAXA: dict[TipoTaxaDI1, str] = {
26
26
 
27
27
 
28
28
  def dados(
29
- datas: DateLike | ArrayLike,
29
+ datas: DateLike | DatesLike,
30
30
  inicio_mes: bool = False,
31
31
  filtrar_pre: bool = False,
32
32
  ) -> pl.DataFrame:
@@ -117,8 +117,8 @@ def dados(
117
117
 
118
118
 
119
119
  def interpolar_taxas(
120
- datas_referencia: DateLike | ArrayLike,
121
- datas_vencimento: DateLike | ArrayLike,
120
+ datas_referencia: DateLike | DatesLike,
121
+ datas_vencimento: DateLike | DatesLike,
122
122
  extrapolar: bool = True,
123
123
  tipo_taxa: TipoTaxaDI1 = "ajuste",
124
124
  ) -> pl.Series:
@@ -223,69 +223,24 @@ def interpolar_taxas(
223
223
  if df_entrada.is_empty():
224
224
  return pl.Series(dtype=pl.Float64)
225
225
 
226
- # Carrega dataset de taxas DI usando datas convertidas do df_entrada
226
+ # Carrega curva de DI para todas as datas únicas do alvo.
227
227
  datas_unicas = df_entrada["data_referencia"].drop_nulls().unique().sort().to_list()
228
228
  df_ref = buscar_historico_cacheado(datas_unicas, "DI1")
229
- # Retorna Series vazia se nenhuma taxa for encontrada
230
229
  if df_ref.is_empty():
231
230
  return pl.Series(dtype=pl.Float64)
232
231
 
233
- # 1. CRIA O ÍNDICE ORIGINAL AQUI
234
- # Isso garante que saberemos a ordem exata depois
235
- df_entrada = df_entrada.with_row_index("_temp_idx")
236
-
237
- # Inicializa taxa_interpolada como None
238
232
  df_entrada = df_entrada.with_columns(
239
233
  dias_uteis=du.contar_expr("data_referencia", "data_vencimento"),
240
- taxa_interpolada=pl.lit(None, dtype=pl.Float64),
241
234
  )
242
235
 
243
- # Lista para armazenar os blocos processados
244
- blocos_processados = []
245
-
246
- # Itera sobre cada data de referência única
247
- for data_ref in df_entrada["data_referencia"].unique():
248
- # 1. Filtra apenas as linhas desta data (Particionamento)
249
- df_parcial = df_entrada.filter(pl.col("data_referencia") == data_ref)
250
-
251
- # 2. Busca as taxas de referência para esta data
252
- df_referencia = df_ref.filter(pl.col("data_referencia") == data_ref)
253
-
254
- # Se não houver dados de curva, adicionamos o bloco como está (com nulos)
255
- # e continuamos.
256
- if df_referencia.is_empty():
257
- blocos_processados.append(df_parcial)
258
- continue
259
-
260
- df_curva = df_referencia.select("dias_uteis", coluna_taxa).drop_nulls()
261
- if df_curva.is_empty():
262
- blocos_processados.append(df_parcial)
263
- continue
264
-
265
- # Inicializa o interpolador com taxas e dias úteis conhecidos
266
- interpolador_du = Interpolador(
267
- dias_uteis=df_curva["dias_uteis"],
268
- taxas=df_curva[coluna_taxa],
269
- metodo="flat_forward",
270
- extrapolar=extrapolar,
271
- )
272
-
273
- df_parcial = df_parcial.with_columns(
274
- pl.col("dias_uteis")
275
- .map_elements(interpolador_du, return_dtype=pl.Float64)
276
- .alias("taxa_interpolada")
277
- )
278
-
279
- blocos_processados.append(df_parcial)
280
-
281
- if not blocos_processados:
282
- return pl.Series(dtype=pl.Float64)
283
-
284
- # 2. CONCATENA E ORDENA DE VOLTA
285
- # O sort("_temp_idx") restaura a ordem original dos inputs
286
- df_saida = pl.concat(blocos_processados).sort("_temp_idx")
287
-
288
- return df_saida["taxa_interpolada"].fill_nan(None)
236
+ return interpolar(
237
+ dus_alvo=df_entrada["dias_uteis"],
238
+ dus_curva=df_ref["dias_uteis"],
239
+ taxas_curva=df_ref[coluna_taxa],
240
+ datas_alvo=df_entrada["data_referencia"],
241
+ datas_curva=df_ref["data_referencia"],
242
+ extrapolar=extrapolar,
243
+ )
289
244
 
290
245
 
291
246
  def interpolar_taxa(
@@ -7,7 +7,7 @@ import pyield._internal.converters as cv
7
7
  from pyield import du
8
8
  from pyield._internal.br_numbers import pct_para_decimal
9
9
  from pyield._internal.data_cache import obter_dataset_cacheado
10
- from pyield._internal.types import ArrayLike, DateLike, any_is_empty
10
+ from pyield._internal.types import DateLike, DatesLike, any_is_empty
11
11
  from pyield.b3._validar_pregao import data_negociacao_valida
12
12
  from pyield.futuro import contratos as ct
13
13
  from pyield.fwd import forwards_expr
@@ -193,7 +193,7 @@ def enriquecer(df: pl.DataFrame, contrato: str) -> pl.DataFrame:
193
193
  return df.sort("data_referencia", "data_vencimento")
194
194
 
195
195
 
196
- def historico(data: DateLike | ArrayLike, contrato: str) -> pl.DataFrame:
196
+ def historico(data: DateLike | DatesLike, contrato: str) -> pl.DataFrame:
197
197
  """Busca histórico de futuros no dataset PR cacheado.
198
198
 
199
199
  Args: