pyield 0.51.2__tar.gz → 0.52.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.
Files changed (70) hide show
  1. {pyield-0.51.2 → pyield-0.52.1}/PKG-INFO +88 -27
  2. {pyield-0.51.2 → pyield-0.52.1}/README.md +87 -26
  3. {pyield-0.51.2 → pyield-0.52.1}/pyield/__init__.py +2 -1
  4. {pyield-0.51.2 → pyield-0.52.1}/pyield/_internal/converters.py +3 -3
  5. {pyield-0.51.2 → pyield-0.52.1}/pyield/_internal/types.py +2 -1
  6. {pyield-0.51.2 → pyield-0.52.1}/pyield/bc/leiloes.py +36 -23
  7. {pyield-0.51.2 → pyield-0.52.1}/pyield/du/core.py +9 -9
  8. {pyield-0.51.2 → pyield-0.52.1}/pyield/futuro/di1.py +14 -59
  9. {pyield-0.51.2 → pyield-0.52.1}/pyield/futuro/historico.py +2 -2
  10. pyield-0.52.1/pyield/interpolador.py +497 -0
  11. {pyield-0.51.2 → pyield-0.52.1}/pyield/tpf/_titulos/lft.py +23 -4
  12. {pyield-0.51.2 → pyield-0.52.1}/pyield/tpf/_titulos/ltn.py +85 -63
  13. {pyield-0.51.2 → pyield-0.52.1}/pyield/tpf/_titulos/ntnb.py +147 -61
  14. {pyield-0.51.2 → pyield-0.52.1}/pyield/tpf/_titulos/ntnb1.py +10 -9
  15. {pyield-0.51.2 → pyield-0.52.1}/pyield/tpf/_titulos/ntnbprinc.py +13 -9
  16. {pyield-0.51.2 → pyield-0.52.1}/pyield/tpf/_titulos/ntnc.py +89 -14
  17. {pyield-0.51.2 → pyield-0.52.1}/pyield/tpf/_titulos/ntnf.py +185 -73
  18. {pyield-0.51.2 → pyield-0.52.1}/pyield/tpf/leiloes.py +41 -32
  19. {pyield-0.51.2 → pyield-0.52.1}/pyield/tpf/pre.py +4 -4
  20. {pyield-0.51.2 → pyield-0.52.1}/pyield/tpf/utils.py +7 -0
  21. {pyield-0.51.2 → pyield-0.52.1}/pyproject.toml +1 -1
  22. pyield-0.51.2/pyield/interpolador.py +0 -263
  23. {pyield-0.51.2 → pyield-0.52.1}/LICENSE +0 -0
  24. {pyield-0.51.2 → pyield-0.52.1}/pyield/_internal/__init__.py +0 -0
  25. {pyield-0.51.2 → pyield-0.52.1}/pyield/_internal/br_numbers.py +0 -0
  26. {pyield-0.51.2 → pyield-0.52.1}/pyield/_internal/cache.py +0 -0
  27. {pyield-0.51.2 → pyield-0.52.1}/pyield/_internal/data_cache.py +0 -0
  28. {pyield-0.51.2 → pyield-0.52.1}/pyield/_internal/retry.py +0 -0
  29. {pyield-0.51.2 → pyield-0.52.1}/pyield/anbima/__init__.py +0 -0
  30. {pyield-0.51.2 → pyield-0.52.1}/pyield/anbima/imaq.py +0 -0
  31. {pyield-0.51.2 → pyield-0.52.1}/pyield/anbima/mercado_secundario.py +0 -0
  32. {pyield-0.51.2 → pyield-0.52.1}/pyield/b3/__init__.py +0 -0
  33. {pyield-0.51.2 → pyield-0.52.1}/pyield/b3/_contratos.py +0 -0
  34. {pyield-0.51.2 → pyield-0.52.1}/pyield/b3/_validar_pregao.py +0 -0
  35. {pyield-0.51.2 → pyield-0.52.1}/pyield/b3/boletim.py +0 -0
  36. {pyield-0.51.2 → pyield-0.52.1}/pyield/b3/derivativos_intradia.py +0 -0
  37. {pyield-0.51.2 → pyield-0.52.1}/pyield/b3/di_over.py +0 -0
  38. {pyield-0.51.2 → pyield-0.52.1}/pyield/bc/__init__.py +0 -0
  39. {pyield-0.51.2 → pyield-0.52.1}/pyield/bc/_olinda.py +0 -0
  40. {pyield-0.51.2 → pyield-0.52.1}/pyield/bc/lft.py +0 -0
  41. {pyield-0.51.2 → pyield-0.52.1}/pyield/bc/sgs.py +0 -0
  42. {pyield-0.51.2 → pyield-0.52.1}/pyield/bc/tpf_intradia.py +0 -0
  43. {pyield-0.51.2 → pyield-0.52.1}/pyield/bc/tpf_mensal.py +0 -0
  44. {pyield-0.51.2 → pyield-0.52.1}/pyield/du/__init__.py +0 -0
  45. {pyield-0.51.2 → pyield-0.52.1}/pyield/du/feriados/__init__.py +0 -0
  46. {pyield-0.51.2 → pyield-0.52.1}/pyield/du/feriados/feriados_antigos_br.txt +0 -0
  47. {pyield-0.51.2 → pyield-0.52.1}/pyield/du/feriados/feriados_br.py +0 -0
  48. {pyield-0.51.2 → pyield-0.52.1}/pyield/du/feriados/feriados_novos_br.txt +0 -0
  49. {pyield-0.51.2 → pyield-0.52.1}/pyield/futuro/__init__.py +0 -0
  50. {pyield-0.51.2 → pyield-0.52.1}/pyield/futuro/contratos.py +0 -0
  51. {pyield-0.51.2 → pyield-0.52.1}/pyield/futuro/intradia.py +0 -0
  52. {pyield-0.51.2 → pyield-0.52.1}/pyield/fwd.py +0 -0
  53. {pyield-0.51.2 → pyield-0.52.1}/pyield/ipca/__init__.py +0 -0
  54. {pyield-0.51.2 → pyield-0.52.1}/pyield/ipca/historico.py +0 -0
  55. {pyield-0.51.2 → pyield-0.52.1}/pyield/ipca/projetado.py +0 -0
  56. {pyield-0.51.2 → pyield-0.52.1}/pyield/py.typed +0 -0
  57. {pyield-0.51.2 → pyield-0.52.1}/pyield/relogio.py +0 -0
  58. {pyield-0.51.2 → pyield-0.52.1}/pyield/selic/__init__.py +0 -0
  59. {pyield-0.51.2 → pyield-0.52.1}/pyield/selic/compromissada.py +0 -0
  60. {pyield-0.51.2 → pyield-0.52.1}/pyield/selic/copom.py +0 -0
  61. {pyield-0.51.2 → pyield-0.52.1}/pyield/selic/cpm.py +0 -0
  62. {pyield-0.51.2 → pyield-0.52.1}/pyield/selic/probabilities.py +0 -0
  63. {pyield-0.51.2 → pyield-0.52.1}/pyield/tpf/__init__.py +0 -0
  64. {pyield-0.51.2 → pyield-0.52.1}/pyield/tpf/_titulos/__init__.py +0 -0
  65. {pyield-0.51.2 → pyield-0.52.1}/pyield/tpf/benchmark.py +0 -0
  66. {pyield-0.51.2 → pyield-0.52.1}/pyield/tpf/rmd/__init__.py +0 -0
  67. {pyield-0.51.2 → pyield-0.52.1}/pyield/tpf/rmd/_aba_1_3.py +0 -0
  68. {pyield-0.51.2 → pyield-0.52.1}/pyield/tpf/rmd/_aba_2_1.py +0 -0
  69. {pyield-0.51.2 → pyield-0.52.1}/pyield/tpf/rmd/_common.py +0 -0
  70. {pyield-0.51.2 → pyield-0.52.1}/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.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
@@ -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
- | `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` |
117
+ | `yd.lft` | módulo | LFT | `dados`, `vencimentos`, `cotacao`, `pu`, `taxa`, `vna`, `rentabilidade`, `rentabilidade_expr` |
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,66 @@ 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
+ - `yd.interpolar(...)` passou a usar `extrapolar=False` como padrão, alinhado
326
+ com `Interpolador`. Antes era `True`. Chamadas que dependiam da
327
+ extrapolação implícita na ponta longa precisam passar `extrapolar=True`
328
+ explicitamente.
329
+ - As funções `dv01(...)` dos títulos públicos agora recebem `pu` como argumento
330
+ explícito. Chamadas antigas que passavam apenas data, vencimento e taxa, ou
331
+ que passavam VNA no caso da NTN-B, precisam ser atualizadas.
332
+ - `ntnf.taxas_zero(...)` mudou os nomes dos parâmetros de curva para o padrão
333
+ `vencimentos_*` / `taxas_*`. Chamadas por posição continuam com a mesma
334
+ ordem; chamadas por keyword precisam usar os novos nomes.
294
335
 
295
- ### Remoção de `dv01_usd`
336
+ ### `Interpolador(...)` não aceita mais lista/Series
296
337
 
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:
338
+ O ``__call__`` e o método ``interpolar`` da classe agora são estritamente
339
+ escalares. O caminho vetorial foi dividido em duas APIs com semântica clara:
299
340
 
300
341
  ```python
301
- df = yd.ntnb.dados("06-06-2026")
302
- dv01_usd = df["dv01"] / yd.ptax("06-06-2026")
342
+ # Antes (v0.51.x)
343
+ interp = yd.Interpolador(dus, taxas, metodo="flat_forward")
344
+ interp([15, 45, 75]) # pl.Series
345
+
346
+ # Agora, dentro de um pipeline Polars
347
+ df.with_columns(taxa=interp.interpolar_expr("du"))
348
+
349
+ # Agora, ad-hoc (curva única ou multi-curva)
350
+ yd.interpolar(
351
+ dus_alvo=pl.Series([15, 45, 75]),
352
+ dus_curva=pl.Series(dus),
353
+ taxas_curva=pl.Series(taxas),
354
+ )
303
355
  ```
304
356
 
305
- ### `ntnb.inflacao_implicita()` `ntnb.implicitas()`
357
+ ### `dv01(...)` agora recebe PU
358
+
359
+ As funções `dv01(...)` dos títulos públicos recebem o PU usado como base para o
360
+ cálculo, não mais o VNA ou apenas a taxa. Se necessário, calcule o PU antes:
361
+
362
+ ```python
363
+ pu = yd.ntnb.pu(vna, yd.ntnb.cotacao(data, vencimento, taxa))
364
+ dv01 = yd.ntnb.dv01(data, vencimento, taxa, pu)
365
+ ```
306
366
 
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`
367
+ ### `ntnf.taxas_zero(...)` usa nomes por conceito
310
368
 
311
- ### Remoção de `ntnb.forward()`
369
+ Os parâmetros de curva da NTN-F foram renomeados para manter o padrão
370
+ `vencimentos_*` / `taxas_*`:
312
371
 
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)`.
372
+ - `ltn_vencimentos` `vencimentos_ltn`
373
+ - `ltn_taxas` `taxas_ltn`
374
+ - `ntnf_vencimentos` → `vencimentos_ntnf`
375
+ - `ntnf_taxas` → `taxas_ntnf`
315
376
 
316
377
  ## Testes
317
378
 
@@ -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
- | `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` |
73
+ | `yd.lft` | módulo | LFT | `dados`, `vencimentos`, `cotacao`, `pu`, `taxa`, `vna`, `rentabilidade`, `rentabilidade_expr` |
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,66 @@ 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
+ - `yd.interpolar(...)` passou a usar `extrapolar=False` como padrão, alinhado
282
+ com `Interpolador`. Antes era `True`. Chamadas que dependiam da
283
+ extrapolação implícita na ponta longa precisam passar `extrapolar=True`
284
+ explicitamente.
285
+ - As funções `dv01(...)` dos títulos públicos agora recebem `pu` como argumento
286
+ explícito. Chamadas antigas que passavam apenas data, vencimento e taxa, ou
287
+ que passavam VNA no caso da NTN-B, precisam ser atualizadas.
288
+ - `ntnf.taxas_zero(...)` mudou os nomes dos parâmetros de curva para o padrão
289
+ `vencimentos_*` / `taxas_*`. Chamadas por posição continuam com a mesma
290
+ ordem; chamadas por keyword precisam usar os novos nomes.
250
291
 
251
- ### Remoção de `dv01_usd`
292
+ ### `Interpolador(...)` não aceita mais lista/Series
252
293
 
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:
294
+ O ``__call__`` e o método ``interpolar`` da classe agora são estritamente
295
+ escalares. O caminho vetorial foi dividido em duas APIs com semântica clara:
255
296
 
256
297
  ```python
257
- df = yd.ntnb.dados("06-06-2026")
258
- dv01_usd = df["dv01"] / yd.ptax("06-06-2026")
298
+ # Antes (v0.51.x)
299
+ interp = yd.Interpolador(dus, taxas, metodo="flat_forward")
300
+ interp([15, 45, 75]) # pl.Series
301
+
302
+ # Agora, dentro de um pipeline Polars
303
+ df.with_columns(taxa=interp.interpolar_expr("du"))
304
+
305
+ # Agora, ad-hoc (curva única ou multi-curva)
306
+ yd.interpolar(
307
+ dus_alvo=pl.Series([15, 45, 75]),
308
+ dus_curva=pl.Series(dus),
309
+ taxas_curva=pl.Series(taxas),
310
+ )
259
311
  ```
260
312
 
261
- ### `ntnb.inflacao_implicita()` `ntnb.implicitas()`
313
+ ### `dv01(...)` agora recebe PU
314
+
315
+ As funções `dv01(...)` dos títulos públicos recebem o PU usado como base para o
316
+ cálculo, não mais o VNA ou apenas a taxa. Se necessário, calcule o PU antes:
317
+
318
+ ```python
319
+ pu = yd.ntnb.pu(vna, yd.ntnb.cotacao(data, vencimento, taxa))
320
+ dv01 = yd.ntnb.dv01(data, vencimento, taxa, pu)
321
+ ```
262
322
 
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`
323
+ ### `ntnf.taxas_zero(...)` usa nomes por conceito
266
324
 
267
- ### Remoção de `ntnb.forward()`
325
+ Os parâmetros de curva da NTN-F foram renomeados para manter o padrão
326
+ `vencimentos_*` / `taxas_*`:
268
327
 
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)`.
328
+ - `ltn_vencimentos` `vencimentos_ltn`
329
+ - `ltn_taxas` `taxas_ltn`
330
+ - `ntnf_vencimentos` → `vencimentos_ntnf`
331
+ - `ntnf_taxas` → `taxas_ntnf`
271
332
 
272
333
  ## Testes
273
334
 
@@ -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(