pyield 0.57.1__tar.gz → 0.58.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.
- {pyield-0.57.1 → pyield-0.58.0}/PKG-INFO +33 -4
- {pyield-0.57.1 → pyield-0.58.0}/README.md +32 -3
- {pyield-0.57.1 → pyield-0.58.0}/pyield/__init__.py +1 -2
- {pyield-0.57.1 → pyield-0.58.0}/pyield/_internal/cache.py +7 -1
- {pyield-0.57.1 → pyield-0.58.0}/pyield/bc/leiloes.py +2 -2
- {pyield-0.57.1 → pyield-0.58.0}/pyield/cpm/__init__.py +4 -0
- pyield-0.58.0/pyield/cpm/probabilidades.py +347 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/fwd.py +20 -68
- {pyield-0.57.1 → pyield-0.58.0}/pyield/interpolador.py +50 -81
- {pyield-0.57.1 → pyield-0.58.0}/pyield/tpf/__init__.py +1 -2
- {pyield-0.57.1 → pyield-0.58.0}/pyield/tpf/leiloes.py +2 -2
- {pyield-0.57.1 → pyield-0.58.0}/pyield/tpf/titulos/_ntnb_bootstrap.py +114 -147
- {pyield-0.57.1 → pyield-0.58.0}/pyield/tpf/titulos/_ntnf_bootstrap.py +3 -2
- {pyield-0.57.1 → pyield-0.58.0}/pyield/tpf/titulos/_utils.py +85 -122
- {pyield-0.57.1 → pyield-0.58.0}/pyield/tpf/titulos/lft.py +65 -48
- {pyield-0.57.1 → pyield-0.58.0}/pyield/tpf/titulos/ltn.py +64 -54
- {pyield-0.57.1 → pyield-0.58.0}/pyield/tpf/titulos/ntnb.py +100 -79
- {pyield-0.57.1 → pyield-0.58.0}/pyield/tpf/titulos/ntnb1.py +121 -57
- {pyield-0.57.1 → pyield-0.58.0}/pyield/tpf/titulos/ntnbp.py +24 -17
- {pyield-0.57.1 → pyield-0.58.0}/pyield/tpf/titulos/ntnc.py +88 -79
- {pyield-0.57.1 → pyield-0.58.0}/pyield/tpf/titulos/ntnf.py +122 -120
- {pyield-0.57.1 → pyield-0.58.0}/pyield/tpf/titulos/pre.py +71 -5
- {pyield-0.57.1 → pyield-0.58.0}/pyield/vna/_consulta.py +17 -5
- {pyield-0.57.1 → pyield-0.58.0}/pyield/vna/_lft.py +2 -2
- {pyield-0.57.1 → pyield-0.58.0}/pyield/vna/_ntnb.py +4 -4
- {pyield-0.57.1 → pyield-0.58.0}/pyield/vna/_ntnc.py +5 -3
- {pyield-0.57.1 → pyield-0.58.0}/pyproject.toml +1 -1
- {pyield-0.57.1 → pyield-0.58.0}/pyproject.toml.orig +1 -1
- pyield-0.57.1/pyield/.DS_Store +0 -0
- pyield-0.57.1/pyield/cpm/probabilidades.py +0 -343
- {pyield-0.57.1 → pyield-0.58.0}/LICENSE +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/_internal/__init__.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/_internal/br_numbers.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/_internal/converters.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/_internal/data_cache.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/_internal/excel.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/_internal/numbers.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/_internal/retry.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/_internal/types.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/anbima/__init__.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/anbima/imaq.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/anbima/taxas.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/b3/__init__.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/b3/_contratos.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/b3/_validar_pregao.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/b3/boletim.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/b3/derivativos_intradia.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/b3/di_over.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/bc/__init__.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/bc/_olinda.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/bc/sgs.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/compromissada.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/copom/__init__.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/copom/_calendario.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/du/__init__.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/du/core.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/du/feriados_br/__init__.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/du/feriados_br/anteriores.txt +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/du/feriados_br/atuais.txt +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/futuro/__init__.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/futuro/contratos.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/futuro/di1.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/futuro/historico.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/futuro/intradia.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/ipca/__init__.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/ipca/historico.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/ipca/projetado.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/py.typed +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/relogio.py +0 -0
- {pyield-0.57.1/pyield/tpf → pyield-0.58.0/pyield}/rmd/__init__.py +0 -0
- {pyield-0.57.1/pyield/tpf → pyield-0.58.0/pyield}/rmd/_aba_1_3.py +0 -0
- {pyield-0.57.1/pyield/tpf → pyield-0.58.0/pyield}/rmd/_aba_2_1.py +0 -0
- {pyield-0.57.1/pyield/tpf → pyield-0.58.0/pyield}/rmd/_common.py +0 -0
- {pyield-0.57.1/pyield/tpf → pyield-0.58.0/pyield}/rmd/_download.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/selic/__init__.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/tpf/_taxas.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/tpf/benchmark.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/tpf/dealers.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/tpf/secundario/__init__.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/tpf/secundario/_intradia.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/tpf/secundario/_mensal.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/tpf/titulos/__init__.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/vna/__init__.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/vna/_calculo.py +0 -0
- {pyield-0.57.1 → pyield-0.58.0}/pyield/vna/_download.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyield
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.58.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
|
|
@@ -84,7 +84,7 @@ uv add pyield
|
|
|
84
84
|
| `yd.ntnb1` | módulo | NTN-B1 (Educa+ e Renda+) | `NomeComercial`, `datas_pagamento`, `fluxos_caixa`, `cotacao`, `cotacao_curva_zero`, `taxa_curva_zero`, `pu`, `duration`, `dv01` |
|
|
85
85
|
| `yd.ntnbp` | módulo | NTN-B Principal | `cotacao`, `taxa`, `pu`, `dv01` |
|
|
86
86
|
| `yd.ntnc` | módulo | NTN-C | `dados`, `datas_pagamento`, `fluxos_caixa`, `cotacao`, `pu`, `taxa`, `duration`, `duration_expr`, `dv01`, `dv01_expr` |
|
|
87
|
-
| `yd.ntnf` | módulo | NTN-F | `dados`, `vencimentos`, `datas_pagamento`, `fluxos_caixa`, `pu`, `taxa`, `taxas_zero`, `premio`, `premio_limpo`, `premio_limpo_expr`, `rentabilidade`, `rentabilidade_expr`, `duration`, `duration_expr`, `dv01`, `dv01_expr` |
|
|
87
|
+
| `yd.ntnf` | módulo | NTN-F | `dados`, `vencimentos`, `datas_pagamento`, `fluxos_caixa`, `pu`, `taxa`, `taxas_zero`, `taxas_zero_forwards`, `premio`, `premio_limpo`, `premio_limpo_expr`, `rentabilidade`, `rentabilidade_expr`, `duration`, `duration_expr`, `dv01`, `dv01_expr` |
|
|
88
88
|
| `yd.vna` | módulo | Valores nominais atualizados dos títulos públicos | `valor`, `historico`, `projetado`, `vigencia` |
|
|
89
89
|
| `yd.copom` | módulo | Calendário automático do Copom | `calendario`, `proxima_reuniao` |
|
|
90
90
|
| `yd.compromissadas(...)` | função | Leilões de operações compromissadas do BCB | `inicio`, `fim` |
|
|
@@ -102,6 +102,32 @@ documentação detalhada e as assinaturas públicas.
|
|
|
102
102
|
|
|
103
103
|
## Compatibilidade da API
|
|
104
104
|
|
|
105
|
+
### Cotações em base 100 — 0.58.0
|
|
106
|
+
|
|
107
|
+
As funções escalares de cálculo dos títulos também aceitam taxas percentuais
|
|
108
|
+
explícitas, como `"5.75%"` ou `"5,75%"`, equivalentes a `0.0575`.
|
|
109
|
+
Números continuam em formato decimal; strings sem `%` são rejeitadas.
|
|
110
|
+
|
|
111
|
+
As funções `cotacao` de `yd.lft`, `yd.ntnb`, `yd.ntnc`, `yd.ntnbp` e
|
|
112
|
+
`yd.ntnb1` passam a retornar base 100 com quatro casas decimais:
|
|
113
|
+
`0.993651` passa a ser `99.3651`. As funções `pu` desses títulos recebem a
|
|
114
|
+
cotação nessa mesma base e aplicam `VNA * cotacao / 100`.
|
|
115
|
+
|
|
116
|
+
A coluna `valor_pagamento` de `fluxos_caixa` de NTN-B, NTN-C e NTN-B1 e o
|
|
117
|
+
retorno de `ntnb1.cotacao_curva_zero` também passam para base 100. Esta última
|
|
118
|
+
mantém a soma sem truncamento para calibrar a TIR equivalente.
|
|
119
|
+
|
|
120
|
+
Para migrar, multiplique cotações e fluxos antigos armazenados por 100, remova
|
|
121
|
+
multiplicações por 100 usadas apenas para exibir cotações e ajuste cálculos
|
|
122
|
+
manuais de PU para dividir por 100. A composição `pu(vna, cotacao(...))`
|
|
123
|
+
preserva a unidade e as regras de precisão do preço. Taxas continuam em formato
|
|
124
|
+
decimal, e VNA e PU continuam em reais.
|
|
125
|
+
|
|
126
|
+
As funções `yd.ltn.dv01` e `yd.ntnf.dv01` não recebem mais um PU de referência:
|
|
127
|
+
calculam a redução do PU teórico para um aumento de 1 ponto-base na taxa.
|
|
128
|
+
|
|
129
|
+
### Demais alterações
|
|
130
|
+
|
|
105
131
|
Este resumo parte da API da `0.56.0`. Mudanças que já faziam parte dessa versão
|
|
106
132
|
ou de versões anteriores estão no histórico das
|
|
107
133
|
[releases do GitHub](https://github.com/crdcj/PYield/releases).
|
|
@@ -119,8 +145,11 @@ ou de versões anteriores estão no histórico das
|
|
|
119
145
|
| `vna_projetado(...)` nos módulos de títulos | `yd.vna.projetado(titulo, data, vna_base, inflacao)` |
|
|
120
146
|
| `vigencia(data)` nos módulos de títulos | `yd.vna.vigencia(titulo, data)` |
|
|
121
147
|
|
|
122
|
-
|
|
123
|
-
|
|
148
|
+
O namespace público segue a autonomia semântica do conceito que o usuário
|
|
149
|
+
precisa conhecer, e não apenas a fonte dos dados ou sua relação temática com
|
|
150
|
+
outro domínio. Por isso, conceitos como `yd.ltn`, `yd.ntnb`, `yd.vna` e `yd.rmd`
|
|
151
|
+
ficam na raiz, enquanto as implementações podem continuar agrupadas
|
|
152
|
+
internamente por domínio para manter o código coeso.
|
|
124
153
|
Consulte as [releases](https://github.com/crdcj/PYield/releases) para o
|
|
125
154
|
histórico completo.
|
|
126
155
|
|
|
@@ -59,7 +59,7 @@ uv add pyield
|
|
|
59
59
|
| `yd.ntnb1` | módulo | NTN-B1 (Educa+ e Renda+) | `NomeComercial`, `datas_pagamento`, `fluxos_caixa`, `cotacao`, `cotacao_curva_zero`, `taxa_curva_zero`, `pu`, `duration`, `dv01` |
|
|
60
60
|
| `yd.ntnbp` | módulo | NTN-B Principal | `cotacao`, `taxa`, `pu`, `dv01` |
|
|
61
61
|
| `yd.ntnc` | módulo | NTN-C | `dados`, `datas_pagamento`, `fluxos_caixa`, `cotacao`, `pu`, `taxa`, `duration`, `duration_expr`, `dv01`, `dv01_expr` |
|
|
62
|
-
| `yd.ntnf` | módulo | NTN-F | `dados`, `vencimentos`, `datas_pagamento`, `fluxos_caixa`, `pu`, `taxa`, `taxas_zero`, `premio`, `premio_limpo`, `premio_limpo_expr`, `rentabilidade`, `rentabilidade_expr`, `duration`, `duration_expr`, `dv01`, `dv01_expr` |
|
|
62
|
+
| `yd.ntnf` | módulo | NTN-F | `dados`, `vencimentos`, `datas_pagamento`, `fluxos_caixa`, `pu`, `taxa`, `taxas_zero`, `taxas_zero_forwards`, `premio`, `premio_limpo`, `premio_limpo_expr`, `rentabilidade`, `rentabilidade_expr`, `duration`, `duration_expr`, `dv01`, `dv01_expr` |
|
|
63
63
|
| `yd.vna` | módulo | Valores nominais atualizados dos títulos públicos | `valor`, `historico`, `projetado`, `vigencia` |
|
|
64
64
|
| `yd.copom` | módulo | Calendário automático do Copom | `calendario`, `proxima_reuniao` |
|
|
65
65
|
| `yd.compromissadas(...)` | função | Leilões de operações compromissadas do BCB | `inicio`, `fim` |
|
|
@@ -77,6 +77,32 @@ documentação detalhada e as assinaturas públicas.
|
|
|
77
77
|
|
|
78
78
|
## Compatibilidade da API
|
|
79
79
|
|
|
80
|
+
### Cotações em base 100 — 0.58.0
|
|
81
|
+
|
|
82
|
+
As funções escalares de cálculo dos títulos também aceitam taxas percentuais
|
|
83
|
+
explícitas, como `"5.75%"` ou `"5,75%"`, equivalentes a `0.0575`.
|
|
84
|
+
Números continuam em formato decimal; strings sem `%` são rejeitadas.
|
|
85
|
+
|
|
86
|
+
As funções `cotacao` de `yd.lft`, `yd.ntnb`, `yd.ntnc`, `yd.ntnbp` e
|
|
87
|
+
`yd.ntnb1` passam a retornar base 100 com quatro casas decimais:
|
|
88
|
+
`0.993651` passa a ser `99.3651`. As funções `pu` desses títulos recebem a
|
|
89
|
+
cotação nessa mesma base e aplicam `VNA * cotacao / 100`.
|
|
90
|
+
|
|
91
|
+
A coluna `valor_pagamento` de `fluxos_caixa` de NTN-B, NTN-C e NTN-B1 e o
|
|
92
|
+
retorno de `ntnb1.cotacao_curva_zero` também passam para base 100. Esta última
|
|
93
|
+
mantém a soma sem truncamento para calibrar a TIR equivalente.
|
|
94
|
+
|
|
95
|
+
Para migrar, multiplique cotações e fluxos antigos armazenados por 100, remova
|
|
96
|
+
multiplicações por 100 usadas apenas para exibir cotações e ajuste cálculos
|
|
97
|
+
manuais de PU para dividir por 100. A composição `pu(vna, cotacao(...))`
|
|
98
|
+
preserva a unidade e as regras de precisão do preço. Taxas continuam em formato
|
|
99
|
+
decimal, e VNA e PU continuam em reais.
|
|
100
|
+
|
|
101
|
+
As funções `yd.ltn.dv01` e `yd.ntnf.dv01` não recebem mais um PU de referência:
|
|
102
|
+
calculam a redução do PU teórico para um aumento de 1 ponto-base na taxa.
|
|
103
|
+
|
|
104
|
+
### Demais alterações
|
|
105
|
+
|
|
80
106
|
Este resumo parte da API da `0.56.0`. Mudanças que já faziam parte dessa versão
|
|
81
107
|
ou de versões anteriores estão no histórico das
|
|
82
108
|
[releases do GitHub](https://github.com/crdcj/PYield/releases).
|
|
@@ -94,8 +120,11 @@ ou de versões anteriores estão no histórico das
|
|
|
94
120
|
| `vna_projetado(...)` nos módulos de títulos | `yd.vna.projetado(titulo, data, vna_base, inflacao)` |
|
|
95
121
|
| `vigencia(data)` nos módulos de títulos | `yd.vna.vigencia(titulo, data)` |
|
|
96
122
|
|
|
97
|
-
|
|
98
|
-
|
|
123
|
+
O namespace público segue a autonomia semântica do conceito que o usuário
|
|
124
|
+
precisa conhecer, e não apenas a fonte dos dados ou sua relação temática com
|
|
125
|
+
outro domínio. Por isso, conceitos como `yd.ltn`, `yd.ntnb`, `yd.vna` e `yd.rmd`
|
|
126
|
+
ficam na raiz, enquanto as implementações podem continuar agrupadas
|
|
127
|
+
internamente por domínio para manter o código coeso.
|
|
99
128
|
Consulte as [releases](https://github.com/crdcj/PYield/releases) para o
|
|
100
129
|
histórico completo.
|
|
101
130
|
|
|
@@ -11,13 +11,12 @@ from pyield.futuro import di1
|
|
|
11
11
|
# Ordem intencional: alguns módulos importam `di1` a partir de `pyield`
|
|
12
12
|
# durante a inicialização do pacote.
|
|
13
13
|
from pyield import cpm, copom, selic, tpf, vna
|
|
14
|
-
from pyield.cpm import probabilidades as _cpm_probabilidades # noqa: F401
|
|
15
14
|
from pyield.bc.sgs import ptax, ptax_serie
|
|
16
15
|
from pyield.compromissada import compromissadas
|
|
17
16
|
from pyield.fwd import forward, forwards, forwards_expr
|
|
18
17
|
from pyield.interpolador import Interpolador, interpolar
|
|
19
18
|
from pyield.relogio import agora, hoje
|
|
20
|
-
from pyield.
|
|
19
|
+
from pyield.rmd import rmd
|
|
21
20
|
from pyield.tpf.titulos import lft, ltn, ntnb, ntnb1, ntnbp, ntnc, ntnf
|
|
22
21
|
|
|
23
22
|
try:
|
|
@@ -15,6 +15,11 @@ def ttl_cache(ttl: int = _TTL_PADRAO, maxsize: int = _TAMANHO_MAXIMO):
|
|
|
15
15
|
Args:
|
|
16
16
|
ttl: Tempo de vida de cada entrada em segundos.
|
|
17
17
|
maxsize: Número máximo de entradas no cache.
|
|
18
|
+
|
|
19
|
+
Notes:
|
|
20
|
+
O wrapper consome _atualizar=True para ignorar e renovar apenas a
|
|
21
|
+
entrada consultada. Erros são propagados, preservando a entrada
|
|
22
|
+
anterior até sua expiração.
|
|
18
23
|
"""
|
|
19
24
|
|
|
20
25
|
def decorador(func):
|
|
@@ -22,9 +27,10 @@ def ttl_cache(ttl: int = _TTL_PADRAO, maxsize: int = _TAMANHO_MAXIMO):
|
|
|
22
27
|
|
|
23
28
|
@wraps(func)
|
|
24
29
|
def wrapper(*args, **kwargs):
|
|
30
|
+
atualizar = kwargs.pop("_atualizar", False)
|
|
25
31
|
chave = (args, tuple(sorted(kwargs.items())))
|
|
26
32
|
agora = time.monotonic()
|
|
27
|
-
if chave in _cache:
|
|
33
|
+
if not atualizar and chave in _cache:
|
|
28
34
|
resultado, expira_em = _cache[chave]
|
|
29
35
|
if agora < expira_em:
|
|
30
36
|
return resultado
|
|
@@ -93,13 +93,13 @@ def _processar_df(df: pl.DataFrame) -> pl.DataFrame:
|
|
|
93
93
|
pl.when(pl.col("titulo") == "LTN")
|
|
94
94
|
.then(
|
|
95
95
|
ltn.dv01_expr(
|
|
96
|
-
"data_liquidacao", "data_vencimento", "taxa_media"
|
|
96
|
+
"data_liquidacao", "data_vencimento", "taxa_media"
|
|
97
97
|
)
|
|
98
98
|
)
|
|
99
99
|
.when(pl.col("titulo") == "NTN-F")
|
|
100
100
|
.then(
|
|
101
101
|
ntnf.dv01_expr(
|
|
102
|
-
"data_liquidacao", "data_vencimento", "taxa_media"
|
|
102
|
+
"data_liquidacao", "data_vencimento", "taxa_media"
|
|
103
103
|
)
|
|
104
104
|
)
|
|
105
105
|
.when(pl.col("titulo") == "NTN-B")
|
|
@@ -350,3 +350,7 @@ def data(date: DateLike) -> pl.DataFrame:
|
|
|
350
350
|
pl.col("preco_ajuste"),
|
|
351
351
|
pl.col("dias_uteis"),
|
|
352
352
|
).sort("data_expiracao", "variacao_strike_bps")
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
# Importado após as definições porque probabilidades utiliza este módulo.
|
|
356
|
+
from pyield.cpm import probabilidades as probabilidades # noqa: E402, PLC0414
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
"""
|
|
2
|
+
probabilidades — Probabilidades implícitas das reuniões do COPOM a partir dos
|
|
3
|
+
preços das opções CPM.
|
|
4
|
+
|
|
5
|
+
O contrato CPM é uma opção europeia cash-or-nothing. No apreçamento neutro ao
|
|
6
|
+
risco, o preço de ajuste da B3 em pontos (0–100) representa a probabilidade
|
|
7
|
+
implícita de cada cenário de alteração da Selic, descontada pela taxa DI até o
|
|
8
|
+
vencimento (Manual de Apreçamento da B3, §3.5).
|
|
9
|
+
|
|
10
|
+
Convenções de probabilidade
|
|
11
|
+
---------------------------
|
|
12
|
+
prob_bruta = preco_ajuste * fator_desconto / 100
|
|
13
|
+
|
|
14
|
+
Probabilidade neutra ao risco direta segundo o Manual da B3 §3.5
|
|
15
|
+
(fórmula invertida):
|
|
16
|
+
|
|
17
|
+
p_n(K) = PR_n * exp(+n * r_n) / N
|
|
18
|
+
|
|
19
|
+
onde
|
|
20
|
+
PR_n = preco_ajuste ("Preço de Referência" da B3, pontos 0–100)
|
|
21
|
+
N = 100 (valor nocional fixo)
|
|
22
|
+
n = dias_uteis / 252 (tempo em anos, convenção de dias úteis)
|
|
23
|
+
r_n = ln(1 + taxa_di1) (taxa DI1 continuamente capitalizada até o
|
|
24
|
+
vencimento)
|
|
25
|
+
taxa_di1 = taxa DI1 interpolada por flat-forward entre a data de
|
|
26
|
+
referência e a data de expiração
|
|
27
|
+
|
|
28
|
+
O preco_ajuste de cpm.data() é o "Preço de Referência" oficial da B3,
|
|
29
|
+
obtido no endpoint CSV: o preço exibido no painel da B3 ("Probabilidades
|
|
30
|
+
da Taxa Selic Meta") e resultado da metodologia P1/P2/P3/P4 da B3. Pode
|
|
31
|
+
ser nulo para datas com mais de aproximadamente um mês, quando o
|
|
32
|
+
endpoint CSV não está disponível.
|
|
33
|
+
|
|
34
|
+
Pode não somar 1,0 por reunião devido ao spread entre oferta e demanda ou
|
|
35
|
+
ao apreçamento P1/P2 da B3. Quando dias_uteis == 0 (o próprio dia da
|
|
36
|
+
reunião), fator_desconto == 1,0 exatamente e prob_bruta se reduz a
|
|
37
|
+
preco_ajuste / 100.
|
|
38
|
+
|
|
39
|
+
prob = prob_bruta / soma(prob_bruta) no grupo de data_expiracao
|
|
40
|
+
Normalizada para que cada reunião some exatamente 1,0. Este é o ajuste de
|
|
41
|
+
apreçamento P3 da B3. Use esta coluna para análise de cenários e gráficos.
|
|
42
|
+
|
|
43
|
+
prob_acumulada = soma acumulada de prob, ordenada por
|
|
44
|
+
variacao_strike_bps em ordem crescente.
|
|
45
|
+
|
|
46
|
+
Observações sobre preco_ajuste nulo
|
|
47
|
+
-----------------------------------
|
|
48
|
+
Às vezes os contratos CPM são listados sem preço de ajuste (não há apreçamento
|
|
49
|
+
oficial da B3 para aquele strike na data, ou o endpoint CSV não está disponível
|
|
50
|
+
para datas antigas). Strikes com preco_ajuste nulo são excluídos da saída de
|
|
51
|
+
probabilidades porque:
|
|
52
|
+
|
|
53
|
+
1. Sua contribuição para a distribuição normalizada é indefinida.
|
|
54
|
+
2. ``group_by().agg(sum())`` do Polars retorna 0,0 (e não nulo) para grupos
|
|
55
|
+
totalmente nulos, o que quebraria a invariável ``prob.sum() == 1.0`` por
|
|
56
|
+
reunião.
|
|
57
|
+
|
|
58
|
+
Como consequência, uma reunião em que TODOS os strikes listados tenham preços
|
|
59
|
+
nulos (por exemplo, CPMH25 no dia da reunião do COPOM de janeiro de 2025) não
|
|
60
|
+
aparecerá na saída. O ranking_reuniao é, portanto, atribuído somente entre as
|
|
61
|
+
reuniões com preços e sempre forma uma sequência consecutiva [1, 2, ..., n].
|
|
62
|
+
|
|
63
|
+
Observações sobre o fallback do DI1
|
|
64
|
+
-----------------------------------
|
|
65
|
+
Se os dados do DI1 não estiverem disponíveis para a data de referência (erro de
|
|
66
|
+
rede, feriado etc.), taxa_di1 assume 0,0 e fator_desconto assume 1,0. Assim,
|
|
67
|
+
prob_bruta se reduz a preco_ajuste / 100, equivalente à fórmula antiga
|
|
68
|
+
(incorreta). Essa degradação é registrada como aviso, mas nunca gera uma
|
|
69
|
+
exceção.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
import logging
|
|
73
|
+
|
|
74
|
+
import polars as pl
|
|
75
|
+
|
|
76
|
+
from pyield import cpm
|
|
77
|
+
from pyield._internal.converters import converter_datas
|
|
78
|
+
from pyield._internal.types import DateLike
|
|
79
|
+
from pyield.futuro import di1
|
|
80
|
+
|
|
81
|
+
logger = logging.getLogger(__name__)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# ---------------------------------------------------------------------------
|
|
85
|
+
# Schema
|
|
86
|
+
# ---------------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _empty_schema() -> pl.DataFrame:
|
|
90
|
+
return pl.DataFrame(
|
|
91
|
+
schema={
|
|
92
|
+
"data_referencia": pl.Date,
|
|
93
|
+
"data_fim_reuniao": pl.Date,
|
|
94
|
+
"data_expiracao": pl.Date,
|
|
95
|
+
"ranking_reuniao": pl.Int32,
|
|
96
|
+
"variacao_strike_bps": pl.Int32,
|
|
97
|
+
"dias_uteis": pl.Int32,
|
|
98
|
+
"preco_ajuste": pl.Float64,
|
|
99
|
+
"taxa_di1": pl.Float64,
|
|
100
|
+
"fator_desconto": pl.Float64,
|
|
101
|
+
"prob_bruta": pl.Float64,
|
|
102
|
+
"prob": pl.Float64,
|
|
103
|
+
"prob_acumulada": pl.Float64,
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# ---------------------------------------------------------------------------
|
|
109
|
+
# Internal helpers
|
|
110
|
+
# ---------------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _add_meeting_rank(df: pl.DataFrame) -> pl.DataFrame:
|
|
114
|
+
"""
|
|
115
|
+
Adiciona ranking_reuniao: 1 = data_expiracao mais próxima, 2 = seguinte, etc.
|
|
116
|
+
Calculado como dense rank sobre data_expiracao.
|
|
117
|
+
"""
|
|
118
|
+
return df.with_columns(
|
|
119
|
+
ranking_reuniao=pl.col("data_expiracao").rank("dense").cast(pl.Int32)
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _add_discount_factors(df: pl.DataFrame) -> pl.DataFrame:
|
|
124
|
+
"""
|
|
125
|
+
Adiciona as colunas de taxa DI1 e fator de desconto.
|
|
126
|
+
|
|
127
|
+
A função usa ``di1.interpolar_taxas()`` de forma vetorizada para buscar
|
|
128
|
+
todas as taxas DI1 em uma única chamada, com uma busca e um interpolador por
|
|
129
|
+
data_referencia única. Em seguida, calcula os fatores de desconto com
|
|
130
|
+
expressões Polars.
|
|
131
|
+
|
|
132
|
+
Método de interpolação
|
|
133
|
+
----------------------
|
|
134
|
+
``di1.interpolar_taxas()`` implementa o Manual da B3 §1.4.2 — Flat Forward
|
|
135
|
+
252, que interpola de forma log-linear os fatores acumulados do DI1 (valores
|
|
136
|
+
de PU):
|
|
137
|
+
|
|
138
|
+
fa_j = (1 + r_j)^(du_j/252) # fator acumulado no vértice j
|
|
139
|
+
fa_k = (1 + r_k)^(du_k/252) # fator acumulado no vértice k
|
|
140
|
+
ft = (du - du_j) / (du_k - du_j) # fração do intervalo
|
|
141
|
+
r = (fa_j * (fa_k / fa_j)^ft)^(252/du) - 1
|
|
142
|
+
|
|
143
|
+
Isso equivale à interpolação log-linear dos preços de ajuste do DI1
|
|
144
|
+
(PU = 100_000 / (1+r)^(du/252)), daí a expressão "interpolação exponencial
|
|
145
|
+
dos preços de ajuste do DI1" no Manual de Apreçamento do CPM §3.5.
|
|
146
|
+
|
|
147
|
+
Não é o método §1.4.1 (Exponencial 252), que interpola diretamente as taxas:
|
|
148
|
+
r = (1 + r_j) * ((1 + r_k)/(1 + r_j))^ft - 1
|
|
149
|
+
Os dois métodos divergem alguns pontos-base em vencimentos intermediários
|
|
150
|
+
(por exemplo, aproximadamente 4,6 pontos-base em du=17 para taxas na faixa
|
|
151
|
+
da Selic em 2026).
|
|
152
|
+
|
|
153
|
+
Na ausência de dados do DI1, usa taxa_di1=0,0 e fator_desconto=1,0.
|
|
154
|
+
"""
|
|
155
|
+
pairs = (
|
|
156
|
+
df.select("data_referencia", "data_expiracao", "dias_uteis")
|
|
157
|
+
.unique(subset=["data_referencia", "data_expiracao"])
|
|
158
|
+
.sort("data_referencia", "data_expiracao")
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
# Chamada vetorizada: um fetch por data_referencia única
|
|
162
|
+
try:
|
|
163
|
+
rates = di1.interpolar_taxas(
|
|
164
|
+
datas_referencia=pairs["data_referencia"],
|
|
165
|
+
datas_vencimento=pairs["data_expiracao"],
|
|
166
|
+
extrapolar=True,
|
|
167
|
+
)
|
|
168
|
+
except Exception:
|
|
169
|
+
logger.warning("Falha na busca DI1; usando fallback taxa=0.0.")
|
|
170
|
+
rates = pl.Series("taxa_interpolada", [None] * len(pairs), dtype=pl.Float64)
|
|
171
|
+
|
|
172
|
+
discount_df = (
|
|
173
|
+
pairs.with_columns(
|
|
174
|
+
taxa_di1=rates.fill_null(0.0).fill_nan(0.0),
|
|
175
|
+
)
|
|
176
|
+
.with_columns(
|
|
177
|
+
fator_desconto=(
|
|
178
|
+
(pl.col("dias_uteis") / 252 * (1 + pl.col("taxa_di1")).log()).exp()
|
|
179
|
+
),
|
|
180
|
+
)
|
|
181
|
+
.select("data_referencia", "data_expiracao", "taxa_di1", "fator_desconto")
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
return df.join(discount_df, on=["data_referencia", "data_expiracao"], how="left")
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _add_probabilities(df: pl.DataFrame) -> pl.DataFrame:
|
|
188
|
+
"""
|
|
189
|
+
Adiciona as colunas prob_bruta, prob e prob_acumulada conforme o Manual da
|
|
190
|
+
B3 §3.5.
|
|
191
|
+
|
|
192
|
+
Assume que o DataFrame já foi filtrado para um tipo de opção e para linhas
|
|
193
|
+
com preco_ajuste não nulo, e que ``_add_discount_factors`` já foi chamada
|
|
194
|
+
para adicionar taxa_di1 e fator_desconto.
|
|
195
|
+
|
|
196
|
+
prob_bruta = preco_ajuste * fator_desconto / 100
|
|
197
|
+
prob = prob_bruta / soma(prob_bruta) no grupo de data_expiracao
|
|
198
|
+
prob_acumulada = soma acumulada de prob, ordenada por
|
|
199
|
+
variacao_strike_bps em ordem crescente.
|
|
200
|
+
"""
|
|
201
|
+
df = _add_discount_factors(df)
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
df.sort("data_expiracao", "variacao_strike_bps")
|
|
205
|
+
.with_columns(
|
|
206
|
+
prob_bruta=(pl.col("preco_ajuste") * pl.col("fator_desconto") / 100),
|
|
207
|
+
)
|
|
208
|
+
.with_columns(
|
|
209
|
+
prob=(
|
|
210
|
+
pl.col("prob_bruta") / pl.col("prob_bruta").sum().over("data_expiracao")
|
|
211
|
+
),
|
|
212
|
+
)
|
|
213
|
+
.with_columns(prob_acumulada=pl.col("prob").cum_sum().over("data_expiracao"))
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
# ---------------------------------------------------------------------------
|
|
218
|
+
# API pública
|
|
219
|
+
# ---------------------------------------------------------------------------
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def all_meetings(
|
|
223
|
+
date: DateLike,
|
|
224
|
+
option_type: str = "call",
|
|
225
|
+
) -> pl.DataFrame:
|
|
226
|
+
"""
|
|
227
|
+
Calcula as probabilidades implícitas de todas as reuniões do COPOM com
|
|
228
|
+
contratos CPM negociados na data informada em `date`.
|
|
229
|
+
|
|
230
|
+
Somente strikes com `preco_ajuste` não nulo são incluídos. Reuniões em que
|
|
231
|
+
todos os strikes listados têm preço nulo são excluídas integralmente (ver
|
|
232
|
+
as observações no nível do módulo).
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
date: Data de referência da consulta; aceita os formatos de `DateLike`.
|
|
236
|
+
option_type: Tipo de opção, `"call"` ou `"put"`. O padrão é `"call"`,
|
|
237
|
+
que na prática é o lado mais líquido.
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
`polars.DataFrame` ordenado por `ranking_reuniao` e
|
|
241
|
+
`variacao_strike_bps`. Em caso de ausência de dados, retorna um
|
|
242
|
+
DataFrame vazio com o schema correto.
|
|
243
|
+
|
|
244
|
+
Output Columns:
|
|
245
|
+
- data_referencia (Date): Data de negociação.
|
|
246
|
+
- data_fim_reuniao (Date): Último dia da reunião do COPOM.
|
|
247
|
+
- data_expiracao (Date): Data de expiração do contrato CPM.
|
|
248
|
+
- ranking_reuniao (Int32): 1 para a reunião mais próxima com preços.
|
|
249
|
+
- variacao_strike_bps (Int32): Variação do strike em pontos-base,
|
|
250
|
+
crescente dentro de cada reunião.
|
|
251
|
+
- dias_uteis (Int32): Dias úteis até a expiração.
|
|
252
|
+
- preco_ajuste (Float64): Preço de Referência da B3 em pontos (0–100).
|
|
253
|
+
- taxa_di1 (Float64): Taxa DI1 interpolada até a expiração.
|
|
254
|
+
- fator_desconto (Float64): Fator aplicado na inversão da fórmula da B3,
|
|
255
|
+
calculado como `exp(dias_uteis / 252 * ln(1 + taxa_di1))`.
|
|
256
|
+
- prob_bruta (Float64): Probabilidade antes da normalização,
|
|
257
|
+
calculada como `preco_ajuste * fator_desconto / 100`.
|
|
258
|
+
- prob (Float64): Probabilidade normalizada, calculada como
|
|
259
|
+
`prob_bruta / soma(prob_bruta)` por `data_expiracao`, com soma
|
|
260
|
+
1,0 por reunião.
|
|
261
|
+
- prob_acumulada (Float64): Probabilidade acumulada em ordem
|
|
262
|
+
crescente de strike.
|
|
263
|
+
|
|
264
|
+
Examples:
|
|
265
|
+
>>> import pyield as yd
|
|
266
|
+
>>> import polars as pl
|
|
267
|
+
>>> df = yd.cpm.probabilidades.all_meetings("29-01-2025") # doctest: +SKIP
|
|
268
|
+
>>> df.is_empty() or df["ranking_reuniao"].min() == 1 # doctest: +SKIP
|
|
269
|
+
True
|
|
270
|
+
>>> sums = df.group_by("data_expiracao").agg(
|
|
271
|
+
... pl.col("prob").sum()
|
|
272
|
+
... ) # doctest: +SKIP
|
|
273
|
+
>>> df.is_empty() or (sums["prob"] - 1.0).abs().max() < 1e-9 # doctest: +SKIP
|
|
274
|
+
True
|
|
275
|
+
"""
|
|
276
|
+
raw = cpm.data(date)
|
|
277
|
+
if raw.is_empty():
|
|
278
|
+
return _empty_schema()
|
|
279
|
+
|
|
280
|
+
df = (
|
|
281
|
+
raw.filter(pl.col("tipo_opcao") == option_type)
|
|
282
|
+
# Excluir strikes sem preço de ajuste — ver docstring do módulo.
|
|
283
|
+
.filter(pl.col("preco_ajuste").is_not_null())
|
|
284
|
+
.pipe(_add_meeting_rank)
|
|
285
|
+
.pipe(_add_probabilities)
|
|
286
|
+
.select(
|
|
287
|
+
"data_referencia",
|
|
288
|
+
"data_fim_reuniao",
|
|
289
|
+
"data_expiracao",
|
|
290
|
+
"ranking_reuniao",
|
|
291
|
+
"variacao_strike_bps",
|
|
292
|
+
"dias_uteis",
|
|
293
|
+
"preco_ajuste",
|
|
294
|
+
"taxa_di1",
|
|
295
|
+
"fator_desconto",
|
|
296
|
+
"prob_bruta",
|
|
297
|
+
"prob",
|
|
298
|
+
"prob_acumulada",
|
|
299
|
+
)
|
|
300
|
+
.sort("ranking_reuniao", "variacao_strike_bps")
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
return df if not df.is_empty() else _empty_schema()
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def meeting(
|
|
307
|
+
date: DateLike,
|
|
308
|
+
expiration: DateLike | None = None,
|
|
309
|
+
option_type: str = "call",
|
|
310
|
+
) -> pl.DataFrame:
|
|
311
|
+
"""
|
|
312
|
+
Calcula as probabilidades implícitas de uma única reunião do COPOM.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
date: Data de referência da consulta.
|
|
316
|
+
expiration: Data de expiração do contrato CPM da reunião desejada, ou
|
|
317
|
+
`None` para selecionar a reunião mais próxima com preços. A expiração
|
|
318
|
+
corresponde ao primeiro dia útil após o término da reunião.
|
|
319
|
+
option_type: Tipo de opção, `"call"` ou `"put"`. O padrão é `"call"`.
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
`polars.DataFrame` com o mesmo schema de `all_meetings()`, filtrado
|
|
323
|
+
para uma única reunião. `ranking_reuniao` é sempre 1 nesta saída,
|
|
324
|
+
relativo à reunião selecionada, e não ao conjunto de reuniões.
|
|
325
|
+
|
|
326
|
+
Examples:
|
|
327
|
+
>>> import pyield as yd
|
|
328
|
+
>>> df = yd.cpm.probabilidades.meeting("29-01-2025") # doctest: +SKIP
|
|
329
|
+
>>> df.is_empty() or abs(df["prob"].sum() - 1.0) < 1e-9 # doctest: +SKIP
|
|
330
|
+
True
|
|
331
|
+
>>> df.is_empty() or df["prob_acumulada"].tail(
|
|
332
|
+
... 1
|
|
333
|
+
... ).item() == 1.0 # doctest: +SKIP
|
|
334
|
+
True
|
|
335
|
+
"""
|
|
336
|
+
df = all_meetings(date, option_type=option_type)
|
|
337
|
+
if df.is_empty():
|
|
338
|
+
return _empty_schema()
|
|
339
|
+
|
|
340
|
+
if expiration is None:
|
|
341
|
+
target_expiry = df.filter(pl.col("ranking_reuniao") == 1)["data_expiracao"][0]
|
|
342
|
+
else:
|
|
343
|
+
target_expiry = converter_datas(expiration)
|
|
344
|
+
|
|
345
|
+
return df.filter(pl.col("data_expiracao") == target_expiry).with_columns(
|
|
346
|
+
ranking_reuniao=pl.lit(1, dtype=pl.Int32)
|
|
347
|
+
)
|
|
@@ -20,15 +20,9 @@ def forwards_expr(
|
|
|
20
20
|
DI1), onde ``agrupar_por`` define janelas independentes de cálculo sem que
|
|
21
21
|
seja necessário extrair colunas, calcular fora e juntar de volta.
|
|
22
22
|
|
|
23
|
-
A
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
\[
|
|
27
|
-
fwd_k = \left( \frac{f_k^{au_k}}{f_j^{au_j}} \right)^{\frac{1}{au_k - au_j}} - 1
|
|
28
|
-
\]
|
|
29
|
-
|
|
30
|
-
Onde ``fₓ = 1 + txₓ`` e ``auₓ = duₓ/252``. A primeira linha de cada grupo
|
|
31
|
-
(menor ``dias_uteis``) é tratada como spot: ``fwd = tx``.
|
|
23
|
+
A taxa a termo entre os vértices ``j`` (anterior) e ``k`` (atual) segue a
|
|
24
|
+
fórmula documentada em :func:`forward`. A primeira linha de cada grupo
|
|
25
|
+
(menor ``dias_uteis``) é tratada como spot: ``f = tx``.
|
|
32
26
|
|
|
33
27
|
Ordenação cronológica:
|
|
34
28
|
A expressão usa ``shift(1).over(agrupar_por, order_by=dias_uteis)``,
|
|
@@ -161,14 +155,13 @@ def forwards_expr(
|
|
|
161
155
|
tx_j = tx_k.shift(1).over(grupo, order_by=du_k)
|
|
162
156
|
au_j = au_k.shift(1).over(grupo, order_by=du_k)
|
|
163
157
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
taxa_forward = (fk**au_k / fj**au_j) ** (1 / (au_k - au_j)) - 1
|
|
158
|
+
ft_k = 1 + tx_k
|
|
159
|
+
ft_j = 1 + tx_j
|
|
160
|
+
f_k = (ft_k**au_k / ft_j**au_j) ** (1 / (au_k - au_j)) - 1
|
|
168
161
|
|
|
169
162
|
# Primeira linha de cada grupo (menor dias_uteis) é a taxa spot
|
|
170
163
|
eh_primeira = du_k == du_k.min().over(grupo)
|
|
171
|
-
return pl.when(eh_primeira).then(tx_k).otherwise(
|
|
164
|
+
return pl.when(eh_primeira).then(tx_k).otherwise(f_k)
|
|
172
165
|
|
|
173
166
|
|
|
174
167
|
def forwards(
|
|
@@ -179,36 +172,10 @@ def forwards(
|
|
|
179
172
|
r"""
|
|
180
173
|
Calcula taxas a termo a partir de taxas zero.
|
|
181
174
|
|
|
182
|
-
A taxa a termo
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
Definindo o fator de capitalização no vértice k como:
|
|
187
|
-
|
|
188
|
-
fₖ = 1 + txₖ
|
|
189
|
-
|
|
190
|
-
A fórmula utilizada é:
|
|
191
|
-
|
|
192
|
-
fwdₖ = (fₖ^(duₖ/252) / fⱼ^(duⱼ/252))^(252/(duₖ - duⱼ)) - 1
|
|
193
|
-
|
|
194
|
-
Como au = du/252 (tempo em anos úteis), a fórmula pode ser simplificada para:
|
|
195
|
-
|
|
196
|
-
fwdₖ = (fₖ^auₖ / fⱼ^auⱼ)^(1/(auₖ - auⱼ)) - 1
|
|
197
|
-
|
|
198
|
-
Em LaTeX, a fórmula é representada como:
|
|
199
|
-
|
|
200
|
-
\[
|
|
201
|
-
fwd_k = \left( \frac{f_k^{au_k}}{f_j^{au_j}} \right)^{\frac{1}{au_k - au_j}} - 1
|
|
202
|
-
\]
|
|
203
|
-
|
|
204
|
-
Onde:
|
|
205
|
-
- fⱼ é o fator de capitalização no vértice anterior (fⱼ = 1 + txⱼ).
|
|
206
|
-
- fₖ é o fator de capitalização no vértice atual (fₖ = 1 + txₖ).
|
|
207
|
-
- txⱼ é a taxa zero para o vértice anterior.
|
|
208
|
-
- txₖ é a taxa zero para o vértice atual.
|
|
209
|
-
- auⱼ é o prazo em anos úteis no vértice anterior (auⱼ = duⱼ/252).
|
|
210
|
-
- auₖ é o prazo em anos úteis no vértice atual (auₖ = duₖ/252).
|
|
211
|
-
- A constante 252 representa o número de dias úteis no ano.
|
|
175
|
+
A taxa a termo em cada vértice é calculada aplicando sucessivamente a
|
|
176
|
+
fórmula documentada em :func:`forward` aos vértices consecutivos. A
|
|
177
|
+
primeira taxa de cada grupo é a taxa zero do primeiro vértice, pois não há
|
|
178
|
+
um vértice anterior para calcular a taxa a termo.
|
|
212
179
|
|
|
213
180
|
A função preserva a ordem original dos dados de entrada. Nulos em
|
|
214
181
|
``dias_uteis`` ou ``taxas`` produzem nulo na linha correspondente; NaN
|
|
@@ -394,29 +361,16 @@ def forward(
|
|
|
394
361
|
r"""
|
|
395
362
|
Calcula a taxa a termo entre dois prazos (dias úteis).
|
|
396
363
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
f₁→₂ = (f₂^(du₂/252) / f₁^(du₁/252))^(252/(du₂ - du₁)) - 1
|
|
400
|
-
|
|
401
|
-
Onde:
|
|
402
|
-
- f₁ é o fator de capitalização do primeiro prazo (f₁ = 1 + tx₁).
|
|
403
|
-
- f₂ é o fator de capitalização do segundo prazo (f₂ = 1 + tx₂).
|
|
404
|
-
- tx₁ é a taxa zero para o primeiro prazo (du₁).
|
|
405
|
-
- tx₂ é a taxa zero para o segundo prazo (du₂).
|
|
406
|
-
- du₁ é o número de dias úteis até a primeira data.
|
|
407
|
-
- du₂ é o número de dias úteis até a segunda data.
|
|
408
|
-
- A constante 252 representa o número de dias úteis no ano.
|
|
409
|
-
|
|
410
|
-
Como au = du/252 (tempo em anos úteis), a fórmula pode ser simplificada para:
|
|
411
|
-
|
|
412
|
-
f₁→₂ = (f₂^au₂ / f₁^au₁)^(1/(au₂ - au₁)) - 1
|
|
413
|
-
|
|
414
|
-
Que em latex fica:
|
|
364
|
+
Definindo \(F_i = 1 + tx_i\) e \(t_i = du_i/252\), onde 252 é o
|
|
365
|
+
número de dias úteis no ano, a fórmula é:
|
|
415
366
|
|
|
416
367
|
\[
|
|
417
|
-
f_{1 \rightarrow 2} = \left( \frac{
|
|
368
|
+
f_{1 \rightarrow 2} = \left( \frac{F_2^{t_2}}{F_1^{t_1}} \right)^{\frac{1}{t_2 - t_1}} - 1
|
|
418
369
|
\]
|
|
419
370
|
|
|
371
|
+
Aqui, ``F₁`` e ``F₂`` são os fatores de capitalização dos prazos ``du1``
|
|
372
|
+
e ``du2``, e ``tx1`` e ``tx2`` são as respectivas taxas zero.
|
|
373
|
+
|
|
420
374
|
Args:
|
|
421
375
|
du1 (int): Número de dias úteis do primeiro ponto (prazo menor).
|
|
422
376
|
du2 (int): Número de dias úteis do segundo ponto (prazo maior).
|
|
@@ -453,9 +407,7 @@ def forward(
|
|
|
453
407
|
au2 = du2 / 252
|
|
454
408
|
|
|
455
409
|
# Definição dos fatores de capitalização:
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
f2 = 1 + taxa2
|
|
410
|
+
ft1 = 1 + taxa1
|
|
411
|
+
ft2 = 1 + taxa2
|
|
459
412
|
|
|
460
|
-
|
|
461
|
-
return (f2**au2 / f1**au1) ** (1 / (au2 - au1)) - 1
|
|
413
|
+
return (ft2**au2 / ft1**au1) ** (1 / (au2 - au1)) - 1
|