pyield 0.55.2__tar.gz → 0.56.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 (82) hide show
  1. {pyield-0.55.2 → pyield-0.56.0}/PKG-INFO +12 -2
  2. {pyield-0.55.2 → pyield-0.56.0}/README.md +11 -1
  3. pyield-0.56.0/pyield/.DS_Store +0 -0
  4. {pyield-0.55.2 → pyield-0.56.0}/pyield/du/core.py +148 -51
  5. {pyield-0.55.2 → pyield-0.56.0}/pyield/du/feriados/feriados_br.py +22 -21
  6. {pyield-0.55.2 → pyield-0.56.0}/pyield/interpolador.py +5 -0
  7. pyield-0.55.2/pyield/tpf/titulos/_zero_td.py → pyield-0.56.0/pyield/tpf/titulos/_bootstrap_forwards.py +19 -5
  8. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/titulos/ntnb.py +10 -7
  9. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/titulos/ntnbp.py +2 -2
  10. {pyield-0.55.2 → pyield-0.56.0}/pyproject.toml +1 -1
  11. {pyield-0.55.2 → pyield-0.56.0}/pyproject.toml.orig +1 -1
  12. {pyield-0.55.2 → pyield-0.56.0}/LICENSE +0 -0
  13. {pyield-0.55.2 → pyield-0.56.0}/pyield/__init__.py +0 -0
  14. {pyield-0.55.2 → pyield-0.56.0}/pyield/_internal/__init__.py +0 -0
  15. {pyield-0.55.2 → pyield-0.56.0}/pyield/_internal/br_numbers.py +0 -0
  16. {pyield-0.55.2 → pyield-0.56.0}/pyield/_internal/cache.py +0 -0
  17. {pyield-0.55.2 → pyield-0.56.0}/pyield/_internal/converters.py +0 -0
  18. {pyield-0.55.2 → pyield-0.56.0}/pyield/_internal/data_cache.py +0 -0
  19. {pyield-0.55.2 → pyield-0.56.0}/pyield/_internal/excel.py +0 -0
  20. {pyield-0.55.2 → pyield-0.56.0}/pyield/_internal/numbers.py +0 -0
  21. {pyield-0.55.2 → pyield-0.56.0}/pyield/_internal/retry.py +0 -0
  22. {pyield-0.55.2 → pyield-0.56.0}/pyield/_internal/types.py +0 -0
  23. {pyield-0.55.2 → pyield-0.56.0}/pyield/anbima/__init__.py +0 -0
  24. {pyield-0.55.2 → pyield-0.56.0}/pyield/anbima/imaq.py +0 -0
  25. {pyield-0.55.2 → pyield-0.56.0}/pyield/anbima/taxas.py +0 -0
  26. {pyield-0.55.2 → pyield-0.56.0}/pyield/b3/__init__.py +0 -0
  27. {pyield-0.55.2 → pyield-0.56.0}/pyield/b3/_contratos.py +0 -0
  28. {pyield-0.55.2 → pyield-0.56.0}/pyield/b3/_validar_pregao.py +0 -0
  29. {pyield-0.55.2 → pyield-0.56.0}/pyield/b3/boletim.py +0 -0
  30. {pyield-0.55.2 → pyield-0.56.0}/pyield/b3/derivativos_intradia.py +0 -0
  31. {pyield-0.55.2 → pyield-0.56.0}/pyield/b3/di_over.py +0 -0
  32. {pyield-0.55.2 → pyield-0.56.0}/pyield/bc/__init__.py +0 -0
  33. {pyield-0.55.2 → pyield-0.56.0}/pyield/bc/_olinda.py +0 -0
  34. {pyield-0.55.2 → pyield-0.56.0}/pyield/bc/leiloes.py +0 -0
  35. {pyield-0.55.2 → pyield-0.56.0}/pyield/bc/lft.py +0 -0
  36. {pyield-0.55.2 → pyield-0.56.0}/pyield/bc/sgs.py +0 -0
  37. {pyield-0.55.2 → pyield-0.56.0}/pyield/du/__init__.py +0 -0
  38. {pyield-0.55.2 → pyield-0.56.0}/pyield/du/feriados/__init__.py +0 -0
  39. {pyield-0.55.2 → pyield-0.56.0}/pyield/du/feriados/feriados_antigos_br.txt +0 -0
  40. {pyield-0.55.2 → pyield-0.56.0}/pyield/du/feriados/feriados_novos_br.txt +0 -0
  41. {pyield-0.55.2 → pyield-0.56.0}/pyield/futuro/__init__.py +0 -0
  42. {pyield-0.55.2 → pyield-0.56.0}/pyield/futuro/contratos.py +0 -0
  43. {pyield-0.55.2 → pyield-0.56.0}/pyield/futuro/di1.py +0 -0
  44. {pyield-0.55.2 → pyield-0.56.0}/pyield/futuro/historico.py +0 -0
  45. {pyield-0.55.2 → pyield-0.56.0}/pyield/futuro/intradia.py +0 -0
  46. {pyield-0.55.2 → pyield-0.56.0}/pyield/fwd.py +0 -0
  47. {pyield-0.55.2 → pyield-0.56.0}/pyield/ipca/__init__.py +0 -0
  48. {pyield-0.55.2 → pyield-0.56.0}/pyield/ipca/historico.py +0 -0
  49. {pyield-0.55.2 → pyield-0.56.0}/pyield/ipca/projetado.py +0 -0
  50. {pyield-0.55.2 → pyield-0.56.0}/pyield/py.typed +0 -0
  51. {pyield-0.55.2 → pyield-0.56.0}/pyield/relogio.py +0 -0
  52. {pyield-0.55.2 → pyield-0.56.0}/pyield/selic/__init__.py +0 -0
  53. {pyield-0.55.2 → pyield-0.56.0}/pyield/selic/compromissada.py +0 -0
  54. {pyield-0.55.2 → pyield-0.56.0}/pyield/selic/copom.py +0 -0
  55. {pyield-0.55.2 → pyield-0.56.0}/pyield/selic/cpm.py +0 -0
  56. {pyield-0.55.2 → pyield-0.56.0}/pyield/selic/probabilities.py +0 -0
  57. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/__init__.py +0 -0
  58. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/_taxas.py +0 -0
  59. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/benchmark.py +0 -0
  60. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/dealers.py +0 -0
  61. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/leiloes.py +0 -0
  62. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/rmd/__init__.py +0 -0
  63. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/rmd/_aba_1_3.py +0 -0
  64. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/rmd/_aba_2_1.py +0 -0
  65. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/rmd/_common.py +0 -0
  66. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/rmd/_download.py +0 -0
  67. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/secundario/__init__.py +0 -0
  68. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/secundario/_intradia.py +0 -0
  69. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/secundario/_mensal.py +0 -0
  70. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/titulos/__init__.py +0 -0
  71. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/titulos/_utils.py +0 -0
  72. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/titulos/lft.py +0 -0
  73. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/titulos/ltn.py +0 -0
  74. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/titulos/ntnb1.py +0 -0
  75. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/titulos/ntnc.py +0 -0
  76. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/titulos/ntnf.py +0 -0
  77. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/titulos/pre.py +0 -0
  78. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/vna/__init__.py +0 -0
  79. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/vna/_download.py +0 -0
  80. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/vna/calculo.py +0 -0
  81. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/vna/ntnb.py +0 -0
  82. {pyield-0.55.2 → pyield-0.56.0}/pyield/tpf/vna/ntnc.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyield
3
- Version: 0.55.2
3
+ Version: 0.56.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
@@ -142,6 +142,15 @@ du.gerar("22-12-2023", "02-01-2024")
142
142
  du.eh_dia_util("25-12-2023") # -> False (Natal)
143
143
  ```
144
144
 
145
+ Por padrão, `calendario="auto"` seleciona a lista de feriados conforme a data.
146
+ Use `calendario="anterior"` para forçar o regime anterior a 26/12/2023 ou
147
+ `calendario="atual"` para forçar a lista vigente na versão instalada:
148
+
149
+ ```python
150
+ du.contar("20-11-2024", "21-11-2024", calendario="anterior") # -> 1
151
+ du.contar("20-11-2024", "21-11-2024", calendario="atual") # -> 0
152
+ ```
153
+
145
154
  As principais funções de cálculo (`contar`, `deslocar` e `eh_dia_util`)
146
155
  suportam operações vetorizadas com listas, Series ou arrays.
147
156
 
@@ -308,10 +317,11 @@ Documentação completa: [crdcj.github.io/PYield](https://crdcj.github.io/PYield
308
317
 
309
318
  ## Compatibilidade e mudanças da API
310
319
 
311
- A versão atual é `v0.55.0`. As mudanças abaixo exigem atualização de código:
320
+ A versão atual é `v0.56.0`. As mudanças abaixo podem exigir atualização de código:
312
321
 
313
322
  | Versão | Mudança principal |
314
323
  |---|---|
324
+ | `v0.56.0` | As funções de dias úteis adotaram `calendario="auto" \| "anterior" \| "atual"`. Em `du.gerar`, substitua `opcao_feriado` por `calendario`; os valores `"inferir"`, `"antigo"` e `"novo"` correspondem agora a `"auto"`, `"anterior"` e `"atual"`. O padrão passou a ser `"auto"`. `Interpolador` agora levanta `ValueError` quando a curva não contém vértices válidos. |
315
325
  | `v0.55.0` | Funções de PU, cotação e VNA dos títulos passaram a retornar `Decimal` com seis casas. Entradas numéricas aceitam `float` ou `Decimal`. |
316
326
  | `v0.54.5` | `fluxos_caixa` não aceita mais `ajustar_datas_pagamento`; os cronogramas usam datas contratuais. |
317
327
  | `v0.54.2` | `taxas_historicas` foi adicionada e `tpf.taxas(completo=True)` foi removida. |
@@ -117,6 +117,15 @@ du.gerar("22-12-2023", "02-01-2024")
117
117
  du.eh_dia_util("25-12-2023") # -> False (Natal)
118
118
  ```
119
119
 
120
+ Por padrão, `calendario="auto"` seleciona a lista de feriados conforme a data.
121
+ Use `calendario="anterior"` para forçar o regime anterior a 26/12/2023 ou
122
+ `calendario="atual"` para forçar a lista vigente na versão instalada:
123
+
124
+ ```python
125
+ du.contar("20-11-2024", "21-11-2024", calendario="anterior") # -> 1
126
+ du.contar("20-11-2024", "21-11-2024", calendario="atual") # -> 0
127
+ ```
128
+
120
129
  As principais funções de cálculo (`contar`, `deslocar` e `eh_dia_util`)
121
130
  suportam operações vetorizadas com listas, Series ou arrays.
122
131
 
@@ -283,10 +292,11 @@ Documentação completa: [crdcj.github.io/PYield](https://crdcj.github.io/PYield
283
292
 
284
293
  ## Compatibilidade e mudanças da API
285
294
 
286
- A versão atual é `v0.55.0`. As mudanças abaixo exigem atualização de código:
295
+ A versão atual é `v0.56.0`. As mudanças abaixo podem exigir atualização de código:
287
296
 
288
297
  | Versão | Mudança principal |
289
298
  |---|---|
299
+ | `v0.56.0` | As funções de dias úteis adotaram `calendario="auto" \| "anterior" \| "atual"`. Em `du.gerar`, substitua `opcao_feriado` por `calendario`; os valores `"inferir"`, `"antigo"` e `"novo"` correspondem agora a `"auto"`, `"anterior"` e `"atual"`. O padrão passou a ser `"auto"`. `Interpolador` agora levanta `ValueError` quando a curva não contém vértices válidos. |
290
300
  | `v0.55.0` | Funções de PU, cotação e VNA dos títulos passaram a retornar `Decimal` com seis casas. Entradas numéricas aceitam `float` ou `Decimal`. |
291
301
  | `v0.54.5` | `fluxos_caixa` não aceita mais `ajustar_datas_pagamento`; os cronogramas usam datas contratuais. |
292
302
  | `v0.54.2` | `taxas_historicas` foi adicionada e `tpf.taxas(completo=True)` foi removida. |
Binary file
@@ -12,21 +12,35 @@ from pyield.du.feriados.feriados_br import FeriadosBrasil
12
12
  LIMITE_DIA_UTIL = 6
13
13
 
14
14
  feriados_br = FeriadosBrasil()
15
- FERIADOS_ANTIGOS = feriados_br.obter_feriados(opcao_feriado="antigo")
16
- FERIADOS_NOVOS = feriados_br.obter_feriados(opcao_feriado="novo")
15
+ FERIADOS_ANTERIORES = feriados_br.obter_feriados(calendario="anterior")
16
+ FERIADOS_ATUAIS = feriados_br.obter_feriados(calendario="atual")
17
17
  DATA_TRANSICAO = FeriadosBrasil.DATA_TRANSICAO
18
+ Calendario = Literal["auto", "anterior", "atual"]
18
19
 
19
20
 
20
- def _expressao_feriados(expr_data: pl.Expr) -> pl.Expr:
21
- return (
22
- pl.when(expr_data < DATA_TRANSICAO)
23
- .then(pl.lit(FERIADOS_ANTIGOS))
24
- .otherwise(pl.lit(FERIADOS_NOVOS))
25
- )
21
+ def _expressao_feriados(
22
+ expr_data: pl.Expr,
23
+ calendario: Calendario = "auto",
24
+ ) -> pl.Expr:
25
+ match calendario:
26
+ case "anterior":
27
+ return pl.lit(FERIADOS_ANTERIORES)
28
+ case "atual":
29
+ return pl.lit(FERIADOS_ATUAIS)
30
+ case "auto":
31
+ return (
32
+ pl.when(expr_data < DATA_TRANSICAO)
33
+ .then(pl.lit(FERIADOS_ANTERIORES))
34
+ .otherwise(pl.lit(FERIADOS_ATUAIS))
35
+ )
36
+ case _:
37
+ raise ValueError("Opção inválida para calendario.")
26
38
 
27
39
 
28
40
  def contar_expr(
29
- inicio: pl.Expr | str | dt.date, fim: pl.Expr | str | dt.date
41
+ inicio: pl.Expr | str | dt.date,
42
+ fim: pl.Expr | str | dt.date,
43
+ calendario: Calendario = "auto",
30
44
  ) -> pl.Expr:
31
45
  """Cria uma expressão Polars para contar dias úteis (com suporte a LazyFrame).
32
46
 
@@ -36,6 +50,10 @@ def contar_expr(
36
50
  Args:
37
51
  inicio: Nome da coluna, expressão Polars ou data literal.
38
52
  fim: Nome da coluna, expressão Polars ou data literal.
53
+ calendario: Lista de feriados a considerar. ``"anterior"`` usa a lista
54
+ vigente antes de 26-12-2023, ``"atual"`` usa a lista vigente a partir
55
+ dessa data e ``"auto"`` seleciona a lista por linha com base em
56
+ ``inicio``. Padrão: ``"auto"``.
39
57
 
40
58
  Returns:
41
59
  Uma ``pl.Expr`` que resulta em Int64.
@@ -82,25 +100,46 @@ def contar_expr(
82
100
  return pl.business_day_count(
83
101
  start=data_inicio,
84
102
  end=data_fim,
85
- holidays=_expressao_feriados(data_inicio),
103
+ holidays=_expressao_feriados(data_inicio, calendario),
86
104
  ).cast(pl.Int64)
87
105
 
88
106
 
89
107
  @overload
90
- def contar(inicio: DatesLike, fim: DatesLike | DateLike | None) -> pl.Series: ...
108
+ def contar(
109
+ inicio: DatesLike,
110
+ fim: DatesLike | DateLike | None,
111
+ calendario: Calendario = ...,
112
+ ) -> pl.Series: ...
91
113
  @overload
92
- def contar(inicio: DateLike | None, fim: DatesLike) -> pl.Series: ...
114
+ def contar(
115
+ inicio: DateLike | None,
116
+ fim: DatesLike,
117
+ calendario: Calendario = ...,
118
+ ) -> pl.Series: ...
93
119
  @overload
94
- def contar(inicio: DateLike, fim: DateLike) -> int: ...
120
+ def contar(
121
+ inicio: DateLike,
122
+ fim: DateLike,
123
+ calendario: Calendario = ...,
124
+ ) -> int: ...
95
125
  @overload
96
- def contar(inicio: DateLike, fim: None) -> None: ...
126
+ def contar(
127
+ inicio: DateLike,
128
+ fim: None,
129
+ calendario: Calendario = ...,
130
+ ) -> None: ...
97
131
  @overload
98
- def contar(inicio: None, fim: DateLike | None) -> None: ...
132
+ def contar(
133
+ inicio: None,
134
+ fim: DateLike | None,
135
+ calendario: Calendario = ...,
136
+ ) -> None: ...
99
137
 
100
138
 
101
139
  def contar(
102
140
  inicio: None | DateLike | DatesLike,
103
141
  fim: None | DateLike | DatesLike,
142
+ calendario: Calendario = "auto",
104
143
  ) -> None | int | pl.Series:
105
144
  """Conta dias úteis entre ``inicio`` (inclusivo) e ``fim`` (exclusivo).
106
145
 
@@ -112,10 +151,10 @@ def contar(
112
151
  resultado corresponde ao i-ésimo par de (``inicio``, ``fim``) após expansão.
113
152
  Isso garante atribuição segura de volta ao DataFrame de origem.
114
153
 
115
- Regime de feriados: Para cada valor de ``inicio``, a lista de feriados (antiga vs.
116
- nova) é escolhida com base na data de transição 2023-12-26 (``DATA_TRANSICAO``).
117
- Datas de início antes da transição usam a lista antiga; datas na transição ou
118
- após usam a lista nova.
154
+ Regime de feriados: Por padrão, para cada valor de ``inicio``, a lista de
155
+ feriados (anterior vs. atual) é escolhida com base na data de transição 2023-12-26
156
+ (``DATA_TRANSICAO``). Também é possível selecionar explicitamente uma das listas
157
+ para toda a contagem.
119
158
 
120
159
  Propagação de nulos: Se qualquer argumento escalar for nulo, retorna ``None``.
121
160
  Nulos dentro de arrays de entrada produzem nulos nas posições correspondentes
@@ -129,6 +168,10 @@ def contar(
129
168
  Args:
130
169
  inicio: Data única ou coleção (limite inclusivo).
131
170
  fim: Data única ou coleção (limite exclusivo).
171
+ calendario: Lista de feriados a considerar. ``"anterior"`` usa a lista
172
+ vigente antes de 26-12-2023, ``"atual"`` usa a lista vigente a partir
173
+ dessa data e ``"auto"`` seleciona a lista por elemento com base em
174
+ ``inicio``. Padrão: ``"auto"``.
132
175
 
133
176
  Returns:
134
177
  Inteiro ou ``None`` se ``inicio`` e ``fim`` forem datas únicas, ou Series
@@ -136,7 +179,8 @@ def contar(
136
179
 
137
180
  Notes:
138
181
  - Esta função é um encapsulamento de ``polars.business_day_count``.
139
- - A lista de feriados é determinada por linha com base na data ``inicio``.
182
+ - Com ``calendario="auto"``, a lista é determinada por linha com base na
183
+ data ``inicio``.
140
184
  - Strings de data aceitas: ``DD-MM-YYYY``, ``DD/MM/YYYY`` e ``YYYY-MM-DD``.
141
185
  - Strings inválidas são tratadas como ``null`` e propagadas ao resultado.
142
186
 
@@ -151,6 +195,10 @@ def contar(
151
195
  >>> du.contar("20-11-2024", "21-11-2024")
152
196
  0
153
197
 
198
+ Seleção explícita da lista anterior, que não inclui 20 de novembro:
199
+ >>> du.contar("20-11-2024", "21-11-2024", calendario="anterior")
200
+ 1
201
+
154
202
  Contagem negativa quando ``inicio`` é posterior a ``fim``:
155
203
  >>> du.contar("08-01-2023", "01-01-2023")
156
204
  -5
@@ -210,7 +258,7 @@ def contar(
210
258
  data={"inicio": inicio, "fim": fim},
211
259
  nan_to_null=True,
212
260
  )
213
- .select(dias_uteis=contar_expr("inicio", "fim"))
261
+ .select(dias_uteis=contar_expr("inicio", "fim", calendario))
214
262
  .get_column("dias_uteis")
215
263
  )
216
264
 
@@ -224,6 +272,7 @@ def deslocar_expr(
224
272
  data: pl.Expr | str,
225
273
  deslocamento: int | pl.Expr | str,
226
274
  rolagem: Literal["forward", "backward"] = "forward",
275
+ calendario: Calendario = "auto",
227
276
  ) -> pl.Expr:
228
277
  """Cria uma expressão Polars para somar dias úteis.
229
278
 
@@ -234,6 +283,10 @@ def deslocar_expr(
234
283
  deslocamento: Número de dias úteis a somar. Pode ser um inteiro fixo ou
235
284
  outra coluna.
236
285
  rolagem: Como tratar a data inicial se ela cair em fim de semana/feriado.
286
+ calendario: Lista de feriados a considerar. ``"anterior"`` usa a lista
287
+ vigente antes de 26-12-2023, ``"atual"`` usa a lista vigente a partir
288
+ dessa data e ``"auto"`` seleciona a lista por linha com base em
289
+ ``data``. Padrão: ``"auto"``.
237
290
 
238
291
  Returns:
239
292
  Uma ``pl.Expr`` que resulta em Date.
@@ -280,7 +333,7 @@ def deslocar_expr(
280
333
  return data_expr.dt.add_business_days(
281
334
  n=deslocamento,
282
335
  roll=rolagem,
283
- holidays=_expressao_feriados(data_expr),
336
+ holidays=_expressao_feriados(data_expr, calendario),
284
337
  )
285
338
 
286
339
 
@@ -289,30 +342,35 @@ def deslocar(
289
342
  datas: DatesLike,
290
343
  deslocamento: ArrayLike | int | None,
291
344
  rolagem: Literal["forward", "backward"] = ...,
345
+ calendario: Calendario = ...,
292
346
  ) -> pl.Series: ...
293
347
  @overload
294
348
  def deslocar(
295
349
  datas: DateLike | None,
296
350
  deslocamento: ArrayLike,
297
351
  rolagem: Literal["forward", "backward"] = ...,
352
+ calendario: Calendario = ...,
298
353
  ) -> pl.Series: ...
299
354
  @overload
300
355
  def deslocar(
301
356
  datas: DateLike,
302
357
  deslocamento: int,
303
358
  rolagem: Literal["forward", "backward"] = ...,
359
+ calendario: Calendario = ...,
304
360
  ) -> dt.date: ...
305
361
  @overload
306
362
  def deslocar(
307
363
  datas: None,
308
364
  deslocamento: int,
309
365
  rolagem: Literal["forward", "backward"] = ...,
366
+ calendario: Calendario = ...,
310
367
  ) -> None: ...
311
368
  @overload
312
369
  def deslocar(
313
370
  datas: DateLike,
314
371
  deslocamento: None,
315
372
  rolagem: Literal["forward", "backward"] = ...,
373
+ calendario: Calendario = ...,
316
374
  ) -> None: ...
317
375
 
318
376
 
@@ -320,6 +378,7 @@ def deslocar(
320
378
  datas: DateLike | DatesLike | None,
321
379
  deslocamento: int | ArrayLike | None,
322
380
  rolagem: Literal["forward", "backward"] = "forward",
381
+ calendario: Calendario = "auto",
323
382
  ) -> dt.date | pl.Series | None:
324
383
  """Desloca data(s) por um número de dias úteis com regime de feriados brasileiro.
325
384
 
@@ -335,10 +394,10 @@ def deslocar(
335
394
  corresponde ao i-ésimo par (data, deslocamento), permitindo atribuição segura
336
395
  de volta ao DataFrame de origem.
337
396
 
338
- Regime de feriados: Para CADA data, a lista de feriados apropriada (antiga vs.
339
- nova) é escolhida com base na data de transição 2023-12-26 (``DATA_TRANSICAO``).
340
- Datas antes da transição usam a lista *antiga*; datas na transição ou após
341
- usam a lista *nova*.
397
+ Regime de feriados: Por padrão, para cada data, a lista de feriados apropriada
398
+ (anterior vs. atual) é escolhida com base na data de transição 2023-12-26
399
+ (``DATA_TRANSICAO``). Também é possível selecionar explicitamente uma das listas
400
+ para todo o deslocamento.
342
401
 
343
402
  Semântica da rolagem: ``rolagem`` só atua quando a data original não é um dia útil
344
403
  sob seu regime. Após o roll, a adição de dias úteis subsequente é aplicada a
@@ -365,6 +424,10 @@ def deslocar(
365
424
  move para frente, negativo para trás, zero mantém a âncora após roll.
366
425
  rolagem: Direção para ajustar uma data inicial não-útil ("forward" ou
367
426
  "backward"). Padrão é "forward".
427
+ calendario: Lista de feriados a considerar. ``"anterior"`` usa a lista
428
+ vigente antes de 26-12-2023, ``"atual"`` usa a lista vigente a partir
429
+ dessa data e ``"auto"`` seleciona a lista por elemento com base em
430
+ ``datas``. Padrão: ``"auto"``.
368
431
 
369
432
  Returns:
370
433
  Um ``date`` Python para entradas escalares, uma Series Polars de datas para
@@ -374,8 +437,8 @@ def deslocar(
374
437
  Notes:
375
438
  - Encapsulamento de ``polars.Expr.dt.add_business_days`` aplicado
376
439
  condicionalmente.
377
- - O regime de feriados é decidido por elemento comparando com
378
- ``DATA_TRANSICAO``.
440
+ - Com ``calendario="auto"``, o regime é decidido por elemento comparando
441
+ com ``DATA_TRANSICAO``.
379
442
  - Fins de semana são sempre tratados como não-úteis.
380
443
  - Strings de data aceitas: ``DD-MM-YYYY``, ``DD/MM/YYYY`` e ``YYYY-MM-DD``.
381
444
  - Strings inválidas são tratadas como ``null`` e propagadas ao resultado.
@@ -389,6 +452,10 @@ def deslocar(
389
452
  >>> du.deslocar("20-11-2024", 0)
390
453
  datetime.date(2024, 11, 21)
391
454
 
455
+ Seleção explícita da lista anterior, que não inclui 20 de novembro:
456
+ >>> du.deslocar("20-11-2024", 0, calendario="anterior")
457
+ datetime.date(2024, 11, 20)
458
+
392
459
  Desloca sábado antes do Natal para o próximo dia útil (terça após Natal):
393
460
  >>> du.deslocar("23-12-2023", 0)
394
461
  datetime.date(2023, 12, 26)
@@ -484,7 +551,10 @@ def deslocar(
484
551
  )
485
552
  .select(
486
553
  data_ajustada=deslocar_expr(
487
- "datas", deslocamento="deslocamento", rolagem=rolagem
554
+ "datas",
555
+ deslocamento="deslocamento",
556
+ rolagem=rolagem,
557
+ calendario=calendario,
488
558
  )
489
559
  )
490
560
  .get_column("data_ajustada")
@@ -500,7 +570,7 @@ def gerar(
500
570
  inicio: DateLike | None = None,
501
571
  fim: DateLike | None = None,
502
572
  fechamento: Literal["both", "left", "right", "none"] = "both",
503
- opcao_feriado: Literal["antigo", "novo", "inferir"] = "novo",
573
+ calendario: Calendario = "auto",
504
574
  ) -> pl.Series:
505
575
  """Gera uma Series de dias úteis entre ``inicio`` e ``fim``.
506
576
 
@@ -511,10 +581,10 @@ def gerar(
511
581
  fim: Data final. Se None, usa a data atual.
512
582
  fechamento: Define quais lados do intervalo são fechados (inclusivos).
513
583
  Opções válidas: 'both', 'left', 'right', 'none'. Padrão: 'both'.
514
- opcao_feriado: Especifica a lista de feriados a considerar. Padrão: "novo".
515
- - 'antigo': Usa a lista de feriados vigente antes de 2023-12-26.
516
- - 'novo': Usa a lista de feriados vigente a partir de 2023-12-26.
517
- - 'inferir': Seleciona com base na data ``inicio`` relativa à transição.
584
+ calendario: Especifica a lista de feriados a considerar. Padrão: "auto".
585
+ - 'anterior': Usa a lista de feriados vigente antes de 2023-12-26.
586
+ - 'atual': Usa a lista de feriados vigente a partir de 2023-12-26.
587
+ - 'auto': Seleciona com base na data ``inicio`` relativa à transição.
518
588
 
519
589
  Returns:
520
590
  Series de dias úteis (nome: 'data').
@@ -537,6 +607,10 @@ def gerar(
537
607
  2023-12-29
538
608
  2024-01-02
539
609
  ]
610
+
611
+ Seleção automática do calendário conforme a data inicial:
612
+ >>> len(du.gerar("20-11-2020", "20-11-2020", calendario="auto"))
613
+ 1
540
614
  """
541
615
  hoje = relogio.hoje()
542
616
  data_inicio = cv.converter_datas(inicio) or hoje
@@ -549,18 +623,25 @@ def gerar(
549
623
 
550
624
  # Pega feriados aplicáveis
551
625
  feriados = feriados_br.obter_feriados(
552
- datas=data_inicio, opcao_feriado=opcao_feriado
626
+ datas=data_inicio, calendario=calendario
553
627
  )
554
628
 
555
629
  # Filtra: só dias úteis (seg-sex e não feriado)
556
630
  return s.filter((s.dt.weekday() < LIMITE_DIA_UTIL) & (~s.is_in(feriados)))
557
631
 
558
632
 
559
- def eh_dia_util_expr(data: pl.Expr | str) -> pl.Expr:
633
+ def eh_dia_util_expr(
634
+ data: pl.Expr | str,
635
+ calendario: Calendario = "auto",
636
+ ) -> pl.Expr:
560
637
  """Cria expressão Polars para verificar se é dia útil (True/False).
561
638
 
562
639
  Args:
563
640
  data: Coluna de datas ou expressão Polars.
641
+ calendario: Lista de feriados a considerar. ``"anterior"`` usa a lista
642
+ vigente antes de 26-12-2023, ``"atual"`` usa a lista vigente a partir
643
+ dessa data e ``"auto"`` seleciona a lista por linha com base em
644
+ ``data``. Padrão: ``"auto"``.
564
645
 
565
646
  Returns:
566
647
  Uma ``pl.Expr`` booleana.
@@ -597,26 +678,31 @@ def eh_dia_util_expr(data: pl.Expr | str) -> pl.Expr:
597
678
  """
598
679
  data_expr = cv.converter_datas_expr(data)
599
680
 
600
- return data_expr.dt.is_business_day(holidays=_expressao_feriados(data_expr))
681
+ return data_expr.dt.is_business_day(
682
+ holidays=_expressao_feriados(data_expr, calendario)
683
+ )
601
684
 
602
685
 
603
686
  @overload
604
- def eh_dia_util(datas: None) -> None: ...
687
+ def eh_dia_util(datas: None, calendario: Calendario = ...) -> None: ...
605
688
  @overload
606
- def eh_dia_util(datas: DateLike) -> bool: ...
689
+ def eh_dia_util(datas: DateLike, calendario: Calendario = ...) -> bool: ...
607
690
  @overload
608
- def eh_dia_util(datas: DatesLike) -> pl.Series: ...
691
+ def eh_dia_util(
692
+ datas: DatesLike, calendario: Calendario = ...
693
+ ) -> pl.Series: ...
609
694
 
610
695
 
611
- def eh_dia_util(datas: None | DateLike | DatesLike) -> None | bool | pl.Series:
696
+ def eh_dia_util(
697
+ datas: None | DateLike | DatesLike,
698
+ calendario: Calendario = "auto",
699
+ ) -> None | bool | pl.Series:
612
700
  """Determina se data(s) são dias úteis brasileiros.
613
701
 
614
- REGIME DE FERIADOS POR LINHA: Para CADA data de entrada, a lista de feriados
615
- apropriada ("antiga" vs. "nova") é selecionada comparando com a data de
616
- transição 2023-12-26 (``DATA_TRANSICAO``). Datas estritamente antes da
617
- transição usam a lista antiga; datas na transição ou após usam a lista nova.
618
- Isso espelha o comportamento de ``contar`` e ``deslocar`` que aplicam a lógica
619
- de regime elemento a elemento.
702
+ REGIME DE FERIADOS: Por padrão, para cada data, a lista de feriados apropriada
703
+ (anterior vs. atual) é escolhida com base na data de transição 2023-12-26
704
+ (``DATA_TRANSICAO``). Também é possível selecionar explicitamente uma das listas
705
+ para toda a avaliação.
620
706
 
621
707
  PRESERVAÇÃO DE ORDEM E FORMA: A saída preserva a ordem original dos elementos.
622
708
  Nenhuma ordenação, deduplicação, remodelação ou alinhamento é realizado; o
@@ -639,6 +725,10 @@ def eh_dia_util(datas: None | DateLike | DatesLike) -> None | bool | pl.Series:
639
725
  Args:
640
726
  datas: Data única ou coleção (list/tuple/Polars Series).
641
727
  Pode incluir nulos que propagam. Entrada escalar nula retorna ``None``.
728
+ calendario: Lista de feriados a considerar. ``"anterior"`` usa a lista
729
+ vigente antes de 26-12-2023, ``"atual"`` usa a lista vigente a partir
730
+ dessa data e ``"auto"`` seleciona a lista por elemento com base em
731
+ ``datas``. Padrão: ``"auto"``.
642
732
 
643
733
  Returns:
644
734
  ``True`` se for dia útil, ``False`` caso contrário para entrada escalar;
@@ -647,10 +737,12 @@ def eh_dia_util(datas: None | DateLike | DatesLike) -> None | bool | pl.Series:
647
737
 
648
738
  Examples:
649
739
  >>> from pyield import du
650
- >>> du.eh_dia_util("25-12-2023") # Natal (calendário antigo)
740
+ >>> du.eh_dia_util("25-12-2023") # Natal (calendário anterior)
651
741
  False
652
- >>> du.eh_dia_util("20-11-2024") # Dia Nacional de Zumbi (novo feriado)
742
+ >>> du.eh_dia_util("20-11-2024") # Dia Nacional de Zumbi
653
743
  False
744
+ >>> du.eh_dia_util("20-11-2024", calendario="anterior")
745
+ True
654
746
  >>> du.eh_dia_util(["22-12-2023", "26-12-2023"]) # Períodos mistos
655
747
  shape: (2,)
656
748
  Series: 'eh_dia_util' [bool]
@@ -661,7 +753,8 @@ def eh_dia_util(datas: None | DateLike | DatesLike) -> None | bool | pl.Series:
661
753
 
662
754
  Notes:
663
755
  - Data de transição definida em ``DATA_TRANSICAO``.
664
- - Espelha a lógica por linha usada em ``contar`` e ``deslocar``.
756
+ - Com ``calendario="auto"``, espelha a lógica por linha usada em
757
+ ``contar`` e ``deslocar``.
665
758
  - Fins de semana sempre avaliam como ``False``.
666
759
  - Elementos nulos propagam.
667
760
  - Strings de data aceitas: ``DD-MM-YYYY``, ``DD/MM/YYYY`` e ``YYYY-MM-DD``.
@@ -669,7 +762,11 @@ def eh_dia_util(datas: None | DateLike | DatesLike) -> None | bool | pl.Series:
669
762
  """
670
763
  s = (
671
764
  pl.DataFrame({"datas": datas}, nan_to_null=True)
672
- .select(eh_dia_util=eh_dia_util_expr("datas"))
765
+ .select(
766
+ eh_dia_util=eh_dia_util_expr(
767
+ "datas", calendario=calendario
768
+ )
769
+ )
673
770
  .get_column("eh_dia_util")
674
771
  )
675
772
 
@@ -8,19 +8,21 @@ import polars as pl
8
8
 
9
9
 
10
10
  class FeriadosBrasil:
11
- """Calendário de feriados nacionais (lista antiga e nova).
11
+ """Calendário de feriados nacionais (lista anterior e atual).
12
12
 
13
13
  Uso interno do módulo `dus`.
14
- DATA_TRANSICAO (inclusive): 2023-12-26. Antes desta data usa lista antiga.
15
- A partir desta data usa lista nova.
14
+ DATA_TRANSICAO (inclusive): 2023-12-26. Antes desta data usa lista anterior.
15
+ A partir desta data usa a lista atual.
16
16
  """
17
17
 
18
18
  DATA_TRANSICAO = dt.date(2023, 12, 26)
19
19
 
20
20
  def __init__(self) -> None:
21
21
  base = Path(__file__).parent
22
- self.feriados_novos = self._carregar_feriados(base / "feriados_novos_br.txt")
23
- self.feriados_antigos = self._carregar_feriados(
22
+ self.feriados_atuais = self._carregar_feriados(
23
+ base / "feriados_novos_br.txt"
24
+ )
25
+ self.feriados_anteriores = self._carregar_feriados(
24
26
  base / "feriados_antigos_br.txt"
25
27
  )
26
28
 
@@ -37,23 +39,22 @@ class FeriadosBrasil:
37
39
  def obter_feriados(
38
40
  self,
39
41
  datas: dt.date | pl.Series | None = None,
40
- opcao_feriado: Literal["antigo", "novo", "inferir"] = "inferir",
42
+ calendario: Literal["auto", "anterior", "atual"] = "auto",
41
43
  ) -> list[dt.date]:
42
- """Retorna a lista de feriados conforme opção ou inferência.
44
+ """Retorna a lista de feriados conforme a opção selecionada.
43
45
 
44
- datas: data única ou série de datas para inferir (quando
45
- opcao_feriado='inferir').
46
- opcao_feriado: 'antigo', 'novo' ou 'inferir'.
46
+ datas: Data única ou série de datas para seleção automática.
47
+ calendario: ``"auto"``, ``"anterior"`` ou ``"atual"``.
47
48
  """
48
- match opcao_feriado:
49
- case "antigo":
50
- return self.feriados_antigos
51
- case "novo":
52
- return self.feriados_novos
53
- case "inferir":
49
+ match calendario:
50
+ case "anterior":
51
+ return self.feriados_anteriores
52
+ case "atual":
53
+ return self.feriados_atuais
54
+ case "auto":
54
55
  if datas is None:
55
56
  raise ValueError(
56
- "'datas' é obrigatório quando opcao_feriado='inferir'."
57
+ "'datas' é obrigatório quando calendario='auto'."
57
58
  )
58
59
  if isinstance(datas, dt.date):
59
60
  data_minima = datas
@@ -61,12 +62,12 @@ class FeriadosBrasil:
61
62
  data_minima = datas.drop_nulls().min()
62
63
 
63
64
  if not isinstance(data_minima, dt.date):
64
- raise ValueError("Não foi possível inferir a data mínima.")
65
+ raise ValueError("Não foi possível selecionar o calendário.")
65
66
 
66
67
  if data_minima < self.DATA_TRANSICAO:
67
- return self.feriados_antigos
68
+ return self.feriados_anteriores
68
69
  else:
69
- return self.feriados_novos
70
+ return self.feriados_atuais
70
71
 
71
72
  case _:
72
- raise ValueError("Opção inválida para opcao_feriado.")
73
+ raise ValueError("Opção inválida para calendario.")
@@ -20,6 +20,9 @@ class Interpolador:
20
20
  abaixo do menor vértice) sempre retorna a primeira taxa conhecida,
21
21
  independentemente desta flag.
22
22
 
23
+ Raises:
24
+ ValueError: Se não houver ao menos um vértice válido na curva.
25
+
23
26
  Notes:
24
27
  - Esta classe usa convenção de 252 dias úteis por ano.
25
28
  - Instâncias desta classe são **imutáveis**. Para modificar as
@@ -68,6 +71,8 @@ class Interpolador:
68
71
  .unique(subset="dus", keep="last")
69
72
  .sort("dus")
70
73
  )
74
+ if df.is_empty():
75
+ raise ValueError("A curva deve conter ao menos um vértice válido.")
71
76
  self._df = df
72
77
  self._method = str(metodo)
73
78
  self._dus = tuple(df.get_column("dus"))
@@ -1,4 +1,4 @@
1
- """Bootstrap da curva zero de NTN-B pelo método TD."""
1
+ """Bootstrap de forwards para a curva zero de NTN-B."""
2
2
 
3
3
  import datetime as dt
4
4
  from collections.abc import Callable
@@ -145,12 +145,13 @@ def taxas_zero(
145
145
  incluir_vertices: bool = False,
146
146
  ) -> pl.DataFrame:
147
147
  r"""
148
- Calcula a curva zero de NTN-B pelo bootstrap de forwards do método TD.
148
+ Calcula a curva zero de NTN-B pelo bootstrap de forwards.
149
149
 
150
150
  O método parte das TIRs observadas das NTN-B, mas não as trata como taxas
151
151
  zero. Ele encontra uma taxa forward para cada vencimento de título para que
152
152
  os fluxos descontados pela curva zero reproduzam exatamente a cotação que a
153
- TIR daquele título produz.
153
+ TIR daquele título produz. A calibração é iterativa e tem convergência
154
+ condicional: depende da existência de um intervalo válido para cada raiz.
154
155
 
155
156
  Notes:
156
157
  **Racional**
@@ -193,7 +194,16 @@ def taxas_zero(
193
194
 
194
195
  Os forwards e taxas zero já calibrados nos títulos curtos permanecem
195
196
  fixos durante a calibração dos títulos longos. Por isso, cada etapa tem
196
- apenas uma incógnita e pode ser resolvida de forma estável por bisseção.
197
+ apenas uma incógnita e é resolvida por bisseção quando há mudança de
198
+ sinal no intervalo pesquisado.
199
+
200
+ **Convergência condicional**
201
+
202
+ A bisseção é determinística quando encontra um intervalo que contém uma
203
+ raiz. A função tenta expandir o limite superior para encontrar esse
204
+ intervalo, mas pode não encontrá-lo para entradas incompatíveis ou
205
+ extremos. Nesse caso, a calibração não produz uma curva e lança
206
+ ``RuntimeError``.
197
207
 
198
208
  **Precisão do método**
199
209
 
@@ -210,9 +220,13 @@ def taxas_zero(
210
220
  Padrão False, retornando apenas os vencimentos informados.
211
221
 
212
222
  Returns:
213
- pl.DataFrame: Curva zero calibrada pelo método TD. Retorna vazio quando
223
+ pl.DataFrame: Curva zero calibrada pelo bootstrap de forwards. Retorna vazio quando
214
224
  não restarem vencimentos posteriores à liquidação.
215
225
 
226
+ Raises:
227
+ RuntimeError: Se não for possível encontrar um intervalo válido para
228
+ alguma taxa forward.
229
+
216
230
  Output Columns:
217
231
  - data_vencimento (Date): Data do vértice da curva.
218
232
  - dias_uteis (Int64): Dias úteis entre liquidação e vértice.
@@ -470,10 +470,12 @@ def taxas_zero(
470
470
  incluir_cupons: bool = False,
471
471
  ) -> pl.DataFrame:
472
472
  """
473
- Calcula as taxas zero da NTN-B usando bootstrap.
473
+ Calcula as taxas zero da NTN-B pelo bootstrap de cupons.
474
474
 
475
- O bootstrap determina as taxas zero a partir dos yields dos títulos,
476
- resolvendo iterativamente as taxas que descontam os fluxos ao preço.
475
+ O método monta uma grade trimestral de datas de pagamento, interpola as TIRs
476
+ dos títulos nos vértices intermediários e resolve sequencialmente as taxas
477
+ zero. Para cada vértice, a taxa é obtida diretamente do preço-alvo e do
478
+ valor presente dos cupons anteriores; não há busca iterativa de raiz.
477
479
 
478
480
  Args:
479
481
  data_liquidacao (DateLike): Data de liquidação.
@@ -551,10 +553,13 @@ def taxas_zero(
551
553
 
552
554
  Notes:
553
555
  O cálculo considera:
554
- - Mapear todas as datas de pagamento até o último vencimento.
556
+ - Mapear todas as datas trimestrais de pagamento até o último vencimento.
555
557
  - Interpolar as TIRs nas datas intermediárias.
556
558
  - Calcular a cotação da NTN-B para cada vencimento.
557
559
  - Calcular as taxas zero reais.
560
+
561
+ Este bootstrap usa a cotação normativa da NTN-B, que arredonda o valor
562
+ presente de cada fluxo e trunca a cotação final em seis casas.
558
563
  """
559
564
  if any_is_empty(data_liquidacao, vencimentos, taxas):
560
565
  return pl.DataFrame()
@@ -589,9 +594,7 @@ def taxas_zero(
589
594
  valor_presente_cupons = _calcular_valor_presente_cupons(
590
595
  df, data_liquidacao, vencimento
591
596
  )
592
- preco_titulo = float(
593
- cotacao(data_liquidacao, vencimento, linha["taxa_tir"])
594
- )
597
+ preco_titulo = float(cotacao(data_liquidacao, vencimento, linha["taxa_tir"]))
595
598
  fator_preco = VALOR_FINAL / (preco_titulo - valor_presente_cupons)
596
599
  taxa_zero = fator_preco ** (1 / linha["anos_uteis"]) - 1
597
600
 
@@ -7,10 +7,10 @@ import polars as pl
7
7
  from pyield import du, interpolador
8
8
  from pyield._internal.numbers import truncar_decimal
9
9
  from pyield._internal.types import DateLike, any_is_empty
10
+ from pyield.tpf.titulos import _bootstrap_forwards
10
11
  from pyield.tpf.titulos import _utils as utils
11
- from pyield.tpf.titulos import _zero_td
12
12
 
13
- taxas_zero = _zero_td.taxas_zero
13
+ taxas_zero = _bootstrap_forwards.taxas_zero
14
14
 
15
15
 
16
16
  def cotacao(
@@ -25,7 +25,7 @@ dependencies = [
25
25
  "fastexcel>=0.19.0",
26
26
  "tzdata>=2024.1; platform_system == 'Windows'",
27
27
  ]
28
- version = "0.55.2"
28
+ version = "0.56.0"
29
29
 
30
30
  [[project.authors]]
31
31
  name = "Carlos Carvalho"
@@ -20,7 +20,7 @@ dependencies = [
20
20
  "fastexcel>=0.19.0",
21
21
  "tzdata>=2024.1; platform_system == 'Windows'",
22
22
  ]
23
- version = "0.55.2"
23
+ version = "0.56.0"
24
24
 
25
25
  [project.urls]
26
26
  Homepage = "https://github.com/crdcj/PYield"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes