pyield 0.57.1__tar.gz → 0.57.2__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 (84) hide show
  1. {pyield-0.57.1 → pyield-0.57.2}/PKG-INFO +1 -1
  2. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/titulos/_ntnb_bootstrap.py +85 -136
  3. {pyield-0.57.1 → pyield-0.57.2}/pyproject.toml +1 -1
  4. {pyield-0.57.1 → pyield-0.57.2}/pyproject.toml.orig +1 -1
  5. {pyield-0.57.1 → pyield-0.57.2}/LICENSE +0 -0
  6. {pyield-0.57.1 → pyield-0.57.2}/README.md +0 -0
  7. {pyield-0.57.1 → pyield-0.57.2}/pyield/.DS_Store +0 -0
  8. {pyield-0.57.1 → pyield-0.57.2}/pyield/__init__.py +0 -0
  9. {pyield-0.57.1 → pyield-0.57.2}/pyield/_internal/__init__.py +0 -0
  10. {pyield-0.57.1 → pyield-0.57.2}/pyield/_internal/br_numbers.py +0 -0
  11. {pyield-0.57.1 → pyield-0.57.2}/pyield/_internal/cache.py +0 -0
  12. {pyield-0.57.1 → pyield-0.57.2}/pyield/_internal/converters.py +0 -0
  13. {pyield-0.57.1 → pyield-0.57.2}/pyield/_internal/data_cache.py +0 -0
  14. {pyield-0.57.1 → pyield-0.57.2}/pyield/_internal/excel.py +0 -0
  15. {pyield-0.57.1 → pyield-0.57.2}/pyield/_internal/numbers.py +0 -0
  16. {pyield-0.57.1 → pyield-0.57.2}/pyield/_internal/retry.py +0 -0
  17. {pyield-0.57.1 → pyield-0.57.2}/pyield/_internal/types.py +0 -0
  18. {pyield-0.57.1 → pyield-0.57.2}/pyield/anbima/__init__.py +0 -0
  19. {pyield-0.57.1 → pyield-0.57.2}/pyield/anbima/imaq.py +0 -0
  20. {pyield-0.57.1 → pyield-0.57.2}/pyield/anbima/taxas.py +0 -0
  21. {pyield-0.57.1 → pyield-0.57.2}/pyield/b3/__init__.py +0 -0
  22. {pyield-0.57.1 → pyield-0.57.2}/pyield/b3/_contratos.py +0 -0
  23. {pyield-0.57.1 → pyield-0.57.2}/pyield/b3/_validar_pregao.py +0 -0
  24. {pyield-0.57.1 → pyield-0.57.2}/pyield/b3/boletim.py +0 -0
  25. {pyield-0.57.1 → pyield-0.57.2}/pyield/b3/derivativos_intradia.py +0 -0
  26. {pyield-0.57.1 → pyield-0.57.2}/pyield/b3/di_over.py +0 -0
  27. {pyield-0.57.1 → pyield-0.57.2}/pyield/bc/__init__.py +0 -0
  28. {pyield-0.57.1 → pyield-0.57.2}/pyield/bc/_olinda.py +0 -0
  29. {pyield-0.57.1 → pyield-0.57.2}/pyield/bc/leiloes.py +0 -0
  30. {pyield-0.57.1 → pyield-0.57.2}/pyield/bc/sgs.py +0 -0
  31. {pyield-0.57.1 → pyield-0.57.2}/pyield/compromissada.py +0 -0
  32. {pyield-0.57.1 → pyield-0.57.2}/pyield/copom/__init__.py +0 -0
  33. {pyield-0.57.1 → pyield-0.57.2}/pyield/copom/_calendario.py +0 -0
  34. {pyield-0.57.1 → pyield-0.57.2}/pyield/cpm/__init__.py +0 -0
  35. {pyield-0.57.1 → pyield-0.57.2}/pyield/cpm/probabilidades.py +0 -0
  36. {pyield-0.57.1 → pyield-0.57.2}/pyield/du/__init__.py +0 -0
  37. {pyield-0.57.1 → pyield-0.57.2}/pyield/du/core.py +0 -0
  38. {pyield-0.57.1 → pyield-0.57.2}/pyield/du/feriados_br/__init__.py +0 -0
  39. {pyield-0.57.1 → pyield-0.57.2}/pyield/du/feriados_br/anteriores.txt +0 -0
  40. {pyield-0.57.1 → pyield-0.57.2}/pyield/du/feriados_br/atuais.txt +0 -0
  41. {pyield-0.57.1 → pyield-0.57.2}/pyield/futuro/__init__.py +0 -0
  42. {pyield-0.57.1 → pyield-0.57.2}/pyield/futuro/contratos.py +0 -0
  43. {pyield-0.57.1 → pyield-0.57.2}/pyield/futuro/di1.py +0 -0
  44. {pyield-0.57.1 → pyield-0.57.2}/pyield/futuro/historico.py +0 -0
  45. {pyield-0.57.1 → pyield-0.57.2}/pyield/futuro/intradia.py +0 -0
  46. {pyield-0.57.1 → pyield-0.57.2}/pyield/fwd.py +0 -0
  47. {pyield-0.57.1 → pyield-0.57.2}/pyield/interpolador.py +0 -0
  48. {pyield-0.57.1 → pyield-0.57.2}/pyield/ipca/__init__.py +0 -0
  49. {pyield-0.57.1 → pyield-0.57.2}/pyield/ipca/historico.py +0 -0
  50. {pyield-0.57.1 → pyield-0.57.2}/pyield/ipca/projetado.py +0 -0
  51. {pyield-0.57.1 → pyield-0.57.2}/pyield/py.typed +0 -0
  52. {pyield-0.57.1 → pyield-0.57.2}/pyield/relogio.py +0 -0
  53. {pyield-0.57.1 → pyield-0.57.2}/pyield/selic/__init__.py +0 -0
  54. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/__init__.py +0 -0
  55. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/_taxas.py +0 -0
  56. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/benchmark.py +0 -0
  57. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/dealers.py +0 -0
  58. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/leiloes.py +0 -0
  59. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/rmd/__init__.py +0 -0
  60. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/rmd/_aba_1_3.py +0 -0
  61. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/rmd/_aba_2_1.py +0 -0
  62. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/rmd/_common.py +0 -0
  63. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/rmd/_download.py +0 -0
  64. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/secundario/__init__.py +0 -0
  65. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/secundario/_intradia.py +0 -0
  66. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/secundario/_mensal.py +0 -0
  67. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/titulos/__init__.py +0 -0
  68. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/titulos/_ntnf_bootstrap.py +0 -0
  69. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/titulos/_utils.py +0 -0
  70. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/titulos/lft.py +0 -0
  71. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/titulos/ltn.py +0 -0
  72. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/titulos/ntnb.py +0 -0
  73. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/titulos/ntnb1.py +0 -0
  74. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/titulos/ntnbp.py +0 -0
  75. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/titulos/ntnc.py +0 -0
  76. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/titulos/ntnf.py +0 -0
  77. {pyield-0.57.1 → pyield-0.57.2}/pyield/tpf/titulos/pre.py +0 -0
  78. {pyield-0.57.1 → pyield-0.57.2}/pyield/vna/__init__.py +0 -0
  79. {pyield-0.57.1 → pyield-0.57.2}/pyield/vna/_calculo.py +0 -0
  80. {pyield-0.57.1 → pyield-0.57.2}/pyield/vna/_consulta.py +0 -0
  81. {pyield-0.57.1 → pyield-0.57.2}/pyield/vna/_download.py +0 -0
  82. {pyield-0.57.1 → pyield-0.57.2}/pyield/vna/_lft.py +0 -0
  83. {pyield-0.57.1 → pyield-0.57.2}/pyield/vna/_ntnb.py +0 -0
  84. {pyield-0.57.1 → pyield-0.57.2}/pyield/vna/_ntnc.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyield
3
- Version: 0.57.1
3
+ Version: 0.57.2
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
@@ -2,7 +2,6 @@
2
2
 
3
3
  import datetime as dt
4
4
  from collections.abc import Callable
5
- from dataclasses import dataclass
6
5
 
7
6
  import polars as pl
8
7
 
@@ -10,45 +9,15 @@ from pyield import du
10
9
  from pyield._internal.types import ArrayLike, DateLike, DatesLike, any_is_empty
11
10
  from pyield.tpf.titulos import _utils as utils
12
11
 
13
- DIA_VENCIMENTO = 15
14
12
  MAX_EXPANSOES_INTERVALO = 32
15
13
 
16
14
 
17
- def _gerar_vertices_mensais(
18
- data_liquidacao: dt.date, ultimo_vencimento: dt.date
15
+ def _gerar_vertices_pagamentos(
16
+ fluxos_titulos: list[pl.DataFrame],
19
17
  ) -> list[dt.date]:
20
- """Gera vértices mensais no dia 15."""
21
- ancora = utils.subtrair_meses(data_liquidacao.replace(day=DIA_VENCIMENTO), 1)
22
- datas = pl.date_range(ancora, ultimo_vencimento, interval="1mo", eager=True)
23
- return datas.filter(datas.is_between(data_liquidacao, ultimo_vencimento)).to_list()
24
-
25
-
26
- def _taxas_zero_por_forwards(
27
- dias_uteis: list[int], taxas_forward: list[float]
28
- ) -> list[float]:
29
- """Acumula taxas zero a partir de forwards constantes por trecho."""
30
- taxas_zero = [taxas_forward[0]]
31
-
32
- for du_anterior, du_atual, taxa_forward in zip(
33
- dias_uteis[:-1], dias_uteis[1:], taxas_forward[1:], strict=True
34
- ):
35
- taxa_zero_anterior = taxas_zero[-1]
36
- fator_acumulado = (1 + taxa_zero_anterior) ** (du_anterior / 252)
37
- fator_forward = (1 + taxa_forward) ** ((du_atual - du_anterior) / 252)
38
- taxas_zero.append((fator_acumulado * fator_forward) ** (252 / du_atual) - 1)
39
-
40
- return taxas_zero
41
-
42
-
43
- def _calcular_cotacao(
44
- fluxos: pl.DataFrame, dias_uteis: pl.Series, taxas: list[float]
45
- ) -> float:
46
- """Calcula a cotação dos fluxos pela taxa de cada prazo."""
47
- return utils.calcular_pv(
48
- fluxos_caixa=fluxos["valor_pagamento"],
49
- taxas=taxas,
50
- prazos=dias_uteis / 252,
51
- )
18
+ """Gera vértices nas datas de pagamento dos títulos."""
19
+ datas = {data for fluxos in fluxos_titulos for data in fluxos["data_pagamento"]}
20
+ return sorted(datas)
52
21
 
53
22
 
54
23
  def _resolver_taxa_forward(
@@ -79,63 +48,84 @@ def _resolver_taxa_forward(
79
48
  return utils._metodo_bissecao(erro, limite_inferior, limite_superior)
80
49
 
81
50
 
82
- def _taxas_forward_vertices(
83
- vertices: list[dt.date],
84
- vencimentos: list[dt.date],
85
- taxas_forward: list[float],
86
- ) -> list[float]:
87
- """Seleciona a taxa do próximo vencimento quando necessário."""
88
- indice_titulo = 0
89
- resultado = []
90
- for vertice in vertices:
91
- while vertice > vencimentos[indice_titulo]:
92
- indice_titulo += 1
93
- resultado.append(taxas_forward[indice_titulo])
94
- return resultado
95
-
96
-
97
- @dataclass
98
- class _ContextoBootstrapForwards:
99
- """Dados compartilhados pela calibração sequencial de forwards."""
100
-
101
- data_liquidacao: dt.date
102
- vertices: list[dt.date]
103
- dias_vertices: list[int]
104
- indice_por_data: dict[dt.date, int]
105
- vencimentos: list[dt.date]
106
- taxas_tir: list[float]
107
- taxas_forward: list[float]
108
-
109
-
110
51
  def _calibrar_taxa_forward(
111
- contexto: _ContextoBootstrapForwards,
112
- indice_titulo: int,
52
+ fluxos: list[tuple[int, float]],
53
+ taxa_tir: float,
54
+ descontos: dict[int, float],
55
+ dias_anterior: int,
56
+ desconto_anterior: float,
113
57
  ) -> float:
114
- """Calibra um forward para reproduzir a cotação de uma NTN-B."""
115
- from pyield.tpf.titulos.ntnb import fluxos_caixa # noqa: PLC0415
116
-
117
- vencimento = contexto.vencimentos[indice_titulo]
118
- fluxos = fluxos_caixa(contexto.data_liquidacao, vencimento)
119
- dias_fluxos = du.contar(contexto.data_liquidacao, fluxos["data_pagamento"])
120
- indices_fluxos = [
121
- contexto.indice_por_data[data] for data in fluxos["data_pagamento"]
122
- ]
123
- cotacao_alvo = _calcular_cotacao(
124
- fluxos, dias_fluxos, [contexto.taxas_tir[indice_titulo]] * fluxos.height
58
+ """Calibra apenas os fluxos posteriores ao vencimento anterior."""
59
+ cotacao_alvo = sum(valor / (1 + taxa_tir) ** (dias / 252) for dias, valor in fluxos)
60
+ pv_fixo = sum(
61
+ valor * descontos[dias] for dias, valor in fluxos if dias <= dias_anterior
125
62
  )
63
+ trecho = [
64
+ ((dias - dias_anterior) / 252, valor * desconto_anterior)
65
+ for dias, valor in fluxos
66
+ if dias > dias_anterior
67
+ ]
126
68
 
127
69
  def erro(taxa_forward: float) -> float:
128
- contexto.taxas_forward[indice_titulo] = taxa_forward
129
- curva_zero = _taxas_zero_por_forwards(
130
- contexto.dias_vertices,
131
- _taxas_forward_vertices(
132
- contexto.vertices, contexto.vencimentos, contexto.taxas_forward
133
- ),
70
+ return (
71
+ pv_fixo
72
+ + sum(valor / (1 + taxa_forward) ** prazo for prazo, valor in trecho)
73
+ - cotacao_alvo
74
+ )
75
+
76
+ return _resolver_taxa_forward(erro, taxa_tir)
77
+
78
+
79
+ def _bootstrap(
80
+ liquidacao: dt.date, titulos: pl.DataFrame, fluxos_titulos: list[pl.DataFrame]
81
+ ) -> pl.DataFrame:
82
+ """Acumula os descontos dos trechos calibrados e retorna a curva nos vencimentos."""
83
+ vertices = _gerar_vertices_pagamentos(fluxos_titulos)
84
+ dias_por_data = dict(
85
+ zip(vertices, du.contar(liquidacao, pl.Series(vertices)), strict=True)
86
+ )
87
+ descontos = {0: 1.0}
88
+ dias_anterior = 0
89
+ desconto_anterior = 1.0
90
+ taxas_forward = []
91
+ taxas_zero = []
92
+ indice_vertice = 0
93
+
94
+ for vencimento, taxa_tir, fluxos in zip(
95
+ titulos["data_vencimento"], titulos["taxa_tir"], fluxos_titulos, strict=True
96
+ ):
97
+ taxa_forward = _calibrar_taxa_forward(
98
+ [(dias_por_data[data], valor) for data, valor in fluxos.iter_rows()],
99
+ taxa_tir,
100
+ descontos,
101
+ dias_anterior,
102
+ desconto_anterior,
134
103
  )
135
- taxas_fluxos = [curva_zero[indice] for indice in indices_fluxos]
136
- return _calcular_cotacao(fluxos, dias_fluxos, taxas_fluxos) - cotacao_alvo
104
+ # Guarda cada desconto uma única vez, após calibrar seu trecho.
105
+ while indice_vertice < len(vertices) and vertices[indice_vertice] <= vencimento:
106
+ dias = dias_por_data[vertices[indice_vertice]]
107
+ descontos[dias] = desconto_anterior / (1 + taxa_forward) ** (
108
+ (dias - dias_anterior) / 252
109
+ )
110
+ indice_vertice += 1
111
+
112
+ dias_vencimento = dias_por_data[vencimento]
113
+ desconto_anterior = descontos[dias_vencimento]
114
+ taxas_forward.append(taxa_forward)
115
+ taxas_zero.append(
116
+ desconto_anterior ** (-252 / dias_vencimento) - 1
117
+ if dias_vencimento
118
+ else taxa_forward
119
+ )
120
+ dias_anterior = dias_vencimento
137
121
 
138
- return _resolver_taxa_forward(erro, contexto.taxas_tir[indice_titulo])
122
+ return titulos.with_columns(
123
+ dias_uteis=pl.Series(
124
+ [dias_por_data[data] for data in titulos["data_vencimento"]], dtype=pl.Int64
125
+ ),
126
+ taxa_forward=pl.Series(taxas_forward, dtype=pl.Float64),
127
+ taxa_zero=pl.Series(taxas_zero, dtype=pl.Float64),
128
+ ).select("data_vencimento", "dias_uteis", "taxa_tir", "taxa_forward", "taxa_zero")
139
129
 
140
130
 
141
131
  def taxas_zero(
@@ -166,9 +156,10 @@ def taxas_zero(
166
156
 
167
157
  **Curva de forwards e taxa zero**
168
158
 
169
- A curva usa vértices mensais no dia 15. Para cada NTN-B, a taxa forward
170
- calibrada é mantida constante desde o vencimento anterior até o seu
171
- vencimento. A primeira taxa forward começa na TIR do título mais curto.
159
+ A curva usa como vértices as datas de pagamento dos títulos informados.
160
+ Para cada NTN-B, a taxa forward calibrada é mantida constante desde o
161
+ vencimento anterior até o seu vencimento. A primeira taxa forward começa
162
+ na TIR do título mais curto.
172
163
 
173
164
  Se \(DU_i\) é o número de dias úteis do vértice \(i\), \(f_i\) é a
174
165
  taxa forward do trecho e \(z_i\) é a taxa zero anualizada, então:
@@ -319,6 +310,7 @@ def taxas_zero(
319
310
  """
320
311
  from pyield.tpf.titulos.ntnb import ( # noqa: PLC0415
321
312
  _validar_entradas_taxas_zero,
313
+ fluxos_caixa,
322
314
  )
323
315
 
324
316
  if any_is_empty(data_liquidacao, vencimentos, taxas):
@@ -342,50 +334,7 @@ def taxas_zero(
342
334
  "data_vencimento"
343
335
  )
344
336
  vencimentos_ordenados = titulos["data_vencimento"].to_list()
345
- taxas_tir = titulos["taxa_tir"].to_list()
346
- ultimo_vencimento = vencimentos_ordenados[-1]
347
- vertices = _gerar_vertices_mensais(liquidacao, ultimo_vencimento)
348
- dias_uteis = du.contar(liquidacao, pl.Series(vertices)).to_list()
349
- indice_por_data = {data: indice for indice, data in enumerate(vertices)}
350
- contexto = _ContextoBootstrapForwards(
351
- liquidacao,
352
- vertices,
353
- dias_uteis,
354
- indice_por_data,
355
- vencimentos_ordenados,
356
- taxas_tir,
357
- taxas_tir.copy(),
358
- )
359
-
360
- for indice_titulo in range(len(vencimentos_ordenados)):
361
- contexto.taxas_forward[indice_titulo] = _calibrar_taxa_forward(
362
- contexto, indice_titulo
363
- )
364
-
365
- forwards_vertices = _taxas_forward_vertices(
366
- vertices, vencimentos_ordenados, contexto.taxas_forward
367
- )
368
- curva_zero = _taxas_zero_por_forwards(dias_uteis, forwards_vertices)
369
- titulos = titulos.with_columns(
370
- taxa_forward=pl.Series(contexto.taxas_forward, dtype=pl.Float64)
371
- )
372
- df = pl.DataFrame(
373
- {
374
- "data_vencimento": vertices,
375
- "dias_uteis": dias_uteis,
376
- "taxa_zero": curva_zero,
377
- }
378
- )
379
-
380
- df = (
381
- df.filter(pl.col("data_vencimento").is_in(vencimentos_ordenados))
382
- .join(titulos, on="data_vencimento", how="left")
383
- .select(
384
- "data_vencimento",
385
- "dias_uteis",
386
- "taxa_tir",
387
- "taxa_forward",
388
- "taxa_zero",
389
- )
390
- )
391
- return df
337
+ fluxos_titulos = [
338
+ fluxos_caixa(liquidacao, vencimento) for vencimento in vencimentos_ordenados
339
+ ]
340
+ return _bootstrap(liquidacao, titulos, fluxos_titulos)
@@ -25,7 +25,7 @@ dependencies = [
25
25
  "fastexcel>=0.19.0",
26
26
  "tzdata>=2024.1; platform_system == 'Windows'",
27
27
  ]
28
- version = "0.57.1"
28
+ version = "0.57.2"
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.57.1"
23
+ version = "0.57.2"
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes