pyield 0.55.0__tar.gz → 0.55.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.
- {pyield-0.55.0 → pyield-0.55.2}/PKG-INFO +12 -128
- {pyield-0.55.0 → pyield-0.55.2}/README.md +11 -127
- {pyield-0.55.0 → pyield-0.55.2}/pyield/selic/copom.py +5 -5
- {pyield-0.55.0 → pyield-0.55.2}/pyield/selic/cpm.py +2 -2
- {pyield-0.55.0 → pyield-0.55.2}/pyield/selic/probabilities.py +11 -7
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/titulos/_zero_td.py +12 -1
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/titulos/lft.py +8 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/titulos/ltn.py +12 -3
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/titulos/ntnb.py +10 -1
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/titulos/ntnbp.py +13 -3
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/titulos/ntnf.py +54 -0
- pyield-0.55.2/pyield/tpf/vna/__init__.py +5 -0
- pyield-0.55.0/pyield/tpf/vna/_utils.py → pyield-0.55.2/pyield/tpf/vna/calculo.py +27 -2
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/vna/ntnb.py +6 -5
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/vna/ntnc.py +6 -5
- {pyield-0.55.0 → pyield-0.55.2}/pyproject.toml +1 -1
- {pyield-0.55.0 → pyield-0.55.2}/pyproject.toml.orig +1 -1
- pyield-0.55.0/pyield/tpf/vna/__init__.py +0 -1
- {pyield-0.55.0 → pyield-0.55.2}/LICENSE +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/__init__.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/_internal/__init__.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/_internal/br_numbers.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/_internal/cache.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/_internal/converters.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/_internal/data_cache.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/_internal/excel.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/_internal/numbers.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/_internal/retry.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/_internal/types.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/anbima/__init__.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/anbima/imaq.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/anbima/taxas.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/b3/__init__.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/b3/_contratos.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/b3/_validar_pregao.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/b3/boletim.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/b3/derivativos_intradia.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/b3/di_over.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/bc/__init__.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/bc/_olinda.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/bc/leiloes.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/bc/lft.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/bc/sgs.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/du/__init__.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/du/core.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/du/feriados/__init__.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/du/feriados/feriados_antigos_br.txt +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/du/feriados/feriados_br.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/du/feriados/feriados_novos_br.txt +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/futuro/__init__.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/futuro/contratos.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/futuro/di1.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/futuro/historico.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/futuro/intradia.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/fwd.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/interpolador.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/ipca/__init__.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/ipca/historico.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/ipca/projetado.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/py.typed +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/relogio.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/selic/__init__.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/selic/compromissada.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/__init__.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/_taxas.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/benchmark.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/dealers.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/leiloes.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/rmd/__init__.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/rmd/_aba_1_3.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/rmd/_aba_2_1.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/rmd/_common.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/rmd/_download.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/secundario/__init__.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/secundario/_intradia.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/secundario/_mensal.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/titulos/__init__.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/titulos/_utils.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/titulos/ntnb1.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/titulos/ntnc.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/titulos/pre.py +0 -0
- {pyield-0.55.0 → pyield-0.55.2}/pyield/tpf/vna/_download.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyield
|
|
3
|
-
Version: 0.55.
|
|
3
|
+
Version: 0.55.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
|
|
@@ -306,136 +306,20 @@ yd.ptax("25-12-2025") # -> nan
|
|
|
306
306
|
|
|
307
307
|
Documentação completa: [crdcj.github.io/PYield](https://crdcj.github.io/PYield/)
|
|
308
308
|
|
|
309
|
-
##
|
|
310
|
-
|
|
311
|
-
Quebras acumuladas por versão desde a v0.52.0.
|
|
312
|
-
|
|
313
|
-
### v0.55.0
|
|
314
|
-
|
|
315
|
-
- `yd.lft.vna(...)`, `yd.lft.cotacao(...)`, `yd.lft.pu(...)`, `yd.ltn.pu(...)`,
|
|
316
|
-
`yd.ntnb.vna(...)`, `yd.ntnb.vna_projetado(...)`, `yd.ntnb.cotacao(...)`,
|
|
317
|
-
`yd.ntnb.pu(...)`, `yd.ntnbp.cotacao(...)`, `yd.ntnbp.pu(...)`,
|
|
318
|
-
`yd.ntnb1.cotacao(...)`, `yd.ntnb1.pu(...)`, `yd.ntnc.vna(...)`,
|
|
319
|
-
`yd.ntnc.vna_projetado(...)`, `yd.ntnc.cotacao(...)`, `yd.ntnc.pu(...)` e
|
|
320
|
-
`yd.ntnf.pu(...)` retornam `Decimal`, preservando exatamente os valores com
|
|
321
|
-
seis casas definidos pela fonte ou pela metodologia. Entradas numéricas
|
|
322
|
-
aceitam `float` ou `Decimal`.
|
|
323
|
-
|
|
324
|
-
### v0.54.5
|
|
325
|
-
|
|
326
|
-
- `yd.ntnf.fluxos_caixa(...)` não aceita mais `ajustar_datas_pagamento`. Os
|
|
327
|
-
cronogramas públicos de NTN-B, NTN-B1, NTN-C e NTN-F usam datas contratuais,
|
|
328
|
-
sem ajuste para dias úteis. Para obter datas efetivas de processamento, use
|
|
329
|
-
`yd.du.deslocar(..., 0)`.
|
|
330
|
-
|
|
331
|
-
### v0.54.2
|
|
332
|
-
|
|
333
|
-
- `yd.tpf.taxas_historicas(...)` foi adicionado para consultas por período ou
|
|
334
|
-
de todo o histórico disponível de taxas indicativas.
|
|
335
|
-
- `yd.tpf.taxas(..., completo=True)` foi removido. `yd.tpf.taxas(...)` mantém
|
|
336
|
-
a visão estável de TPF; para acessar todas as colunas processadas da fonte,
|
|
337
|
-
use `pyield.anbima.taxas.buscar(data)` ou `pyield.anbima.taxas.ler(fonte)`.
|
|
338
|
-
|
|
339
|
-
### v0.54.0
|
|
340
|
-
|
|
341
|
-
- LFT, NTN-B, NTN-C e NTN-B1 passaram a expor **cotação e valores de fluxo em
|
|
342
|
-
base 1** (fator decimal), em vez de base 100 (escala percentual da ANBIMA).
|
|
343
|
-
A precisão numérica é preservada: o truncamento de 4 casas na escala ANBIMA
|
|
344
|
-
equivale a 6 casas em base 1.
|
|
345
|
-
|
|
346
|
-
```python
|
|
347
|
-
# Antes
|
|
348
|
-
yd.ntnb.cotacao("31-05-2024", "15-05-2035", 0.061490) # -> 99.3651
|
|
349
|
-
yd.lft.pu(15785.324502, 99.9291) # -> 15774.132706
|
|
350
|
-
|
|
351
|
-
# Agora
|
|
352
|
-
yd.ntnb.cotacao("31-05-2024", "15-05-2035", 0.061490) # -> Decimal('0.993651')
|
|
353
|
-
yd.lft.pu(15785.324502, 0.999291) # -> Decimal('15774.132706')
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
Também afeta `fluxos_caixa(...)` de NTN-B e NTN-C: o cupom semestral passou
|
|
357
|
-
de `2,956301` para `0,02956301` (NTN-C 6% a.a.) e o pagamento final passou
|
|
358
|
-
de `102,956301` para `1,02956301`. Chamadas a `pu(vna, cotacao)` que
|
|
359
|
-
dividiam por 100 (`cotacao / 100`) devem passar diretamente o fator em
|
|
360
|
-
base 1. O PU final permanece na mesma escala financeira.
|
|
361
|
-
- `yd.ntnbprinc` (e `yd.tpf.ntnbprinc`) foi renomeado para `yd.ntnbp` (e
|
|
362
|
-
`yd.tpf.ntnbp`), alinhando o namespace público aos nomes curtos usados no
|
|
363
|
-
restante da biblioteca (`ntnb`, `ntnb1`, `ntnbp`, `ntnc`, `ntnf`).
|
|
364
|
-
- `yd.tpf.premio_pre` foi renomeado para `yd.tpf.premios_pre`, coerente com o
|
|
365
|
-
retorno tabular (`DataFrame` com múltiplos prêmios).
|
|
366
|
-
- Entradas escalares de data inválidas agora **levantam `ValueError`** em
|
|
367
|
-
qualquer função pública que aceite `DateLike`. Antes, chamadas como
|
|
368
|
-
`yd.du.deslocar("31-02-2024", 1)` retornavam `None` silenciosamente. Em
|
|
369
|
-
operações vetorizadas o parse continua tolerante: elementos inválidos viram
|
|
370
|
-
`null` para preservar o pipeline Polars. Strings vazias ou só com espaços
|
|
371
|
-
continuam sendo tratadas como nulas.
|
|
372
|
-
|
|
373
|
-
### v0.53.0
|
|
374
|
-
|
|
375
|
-
- As funções secundárias de TPF migraram do `yd.tpf` para o namespace dedicado
|
|
376
|
-
`yd.tpf.secundario`:
|
|
377
|
-
|
|
378
|
-
- `yd.tpf.secundario_intradia(...)` → `yd.tpf.secundario.intradia(...)`
|
|
379
|
-
- `yd.tpf.secundario_mensal(...)` → `yd.tpf.secundario.mensal(...)`
|
|
380
|
-
|
|
381
|
-
### v0.52.0
|
|
382
|
-
|
|
383
|
-
- `Interpolador(...)` e `Interpolador.interpolar(...)` agora aceitam **apenas
|
|
384
|
-
inteiro escalar**. Chamadas com lista/`pl.Series` (`interp([30, 60])`),
|
|
385
|
-
antes suportadas via sobrecarga vetorial, agora levantam `TypeError`.
|
|
386
|
-
Substitua por `Interpolador.interpolar_expr` em pipelines Polars ou pela
|
|
387
|
-
função top-level `yd.interpolar(...)` (curva única ou multi-curva).
|
|
388
|
-
- `yd.interpolar(...)` passou a usar `extrapolar=False` como padrão, alinhado
|
|
389
|
-
com `Interpolador`. Antes era `True`. Chamadas que dependiam da
|
|
390
|
-
extrapolação implícita na ponta longa precisam passar `extrapolar=True`
|
|
391
|
-
explicitamente.
|
|
392
|
-
- As funções `dv01(...)` dos títulos públicos agora recebem `pu` como argumento
|
|
393
|
-
explícito. Chamadas antigas que passavam apenas data, vencimento e taxa, ou
|
|
394
|
-
que passavam VNA no caso da NTN-B, precisam ser atualizadas.
|
|
395
|
-
- `ntnf.taxas_zero(...)` mudou os nomes dos parâmetros de curva para o padrão
|
|
396
|
-
`vencimentos_*` / `taxas_*`. Chamadas por posição continuam com a mesma
|
|
397
|
-
ordem; chamadas por keyword precisam usar os novos nomes.
|
|
398
|
-
|
|
399
|
-
#### `Interpolador(...)` não aceita mais lista/Series
|
|
400
|
-
|
|
401
|
-
O ``__call__`` e o método ``interpolar`` da classe agora são estritamente
|
|
402
|
-
escalares. O caminho vetorial foi dividido em duas APIs com semântica clara:
|
|
309
|
+
## Compatibilidade e mudanças da API
|
|
403
310
|
|
|
404
|
-
|
|
405
|
-
# Antes (v0.51.x)
|
|
406
|
-
interp = yd.Interpolador(dus, taxas, metodo="flat_forward")
|
|
407
|
-
interp([15, 45, 75]) # pl.Series
|
|
408
|
-
|
|
409
|
-
# Agora, dentro de um pipeline Polars
|
|
410
|
-
df.with_columns(taxa=interp.interpolar_expr("du"))
|
|
411
|
-
|
|
412
|
-
# Agora, ad-hoc (curva única ou multi-curva)
|
|
413
|
-
yd.interpolar(
|
|
414
|
-
dus_alvo=pl.Series([15, 45, 75]),
|
|
415
|
-
dus_curva=pl.Series(dus),
|
|
416
|
-
taxas_curva=pl.Series(taxas),
|
|
417
|
-
)
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
#### `dv01(...)` agora recebe PU
|
|
421
|
-
|
|
422
|
-
As funções `dv01(...)` dos títulos públicos recebem o PU usado como base para o
|
|
423
|
-
cálculo, não mais o VNA ou apenas a taxa. Se necessário, calcule o PU antes:
|
|
424
|
-
|
|
425
|
-
```python
|
|
426
|
-
pu = yd.ntnb.pu(vna, yd.ntnb.cotacao(data, vencimento, taxa))
|
|
427
|
-
dv01 = yd.ntnb.dv01(data, vencimento, taxa, pu)
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
#### `ntnf.taxas_zero(...)` usa nomes por conceito
|
|
311
|
+
A versão atual é `v0.55.0`. As mudanças abaixo exigem atualização de código:
|
|
431
312
|
|
|
432
|
-
|
|
433
|
-
|
|
313
|
+
| Versão | Mudança principal |
|
|
314
|
+
|---|---|
|
|
315
|
+
| `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
|
+
| `v0.54.5` | `fluxos_caixa` não aceita mais `ajustar_datas_pagamento`; os cronogramas usam datas contratuais. |
|
|
317
|
+
| `v0.54.2` | `taxas_historicas` foi adicionada e `tpf.taxas(completo=True)` foi removida. |
|
|
318
|
+
| `v0.54.0` | Cotações e fluxos de LFT, NTN-B, NTN-C e NTN-B1 passaram de base 100 para base 1; `ntnbprinc` virou `ntnbp`; `premio_pre` virou `premios_pre`; datas escalares inválidas passaram a levantar `ValueError`. |
|
|
319
|
+
| `v0.53.0` | O mercado secundário de TPF passou para `yd.tpf.secundario.intradia` e `yd.tpf.secundario.mensal`. |
|
|
320
|
+
| `v0.52.0` | `Interpolador` passou a ser escalar; use `interpolar_expr` ou `yd.interpolar` para vetores. `dv01` passou a exigir `pu` e `ntnf.taxas_zero` adotou nomes `vencimentos_*` / `taxas_*`. |
|
|
434
321
|
|
|
435
|
-
|
|
436
|
-
- `ltn_taxas` → `taxas_ltn`
|
|
437
|
-
- `ntnf_vencimentos` → `vencimentos_ntnf`
|
|
438
|
-
- `ntnf_taxas` → `taxas_ntnf`
|
|
322
|
+
O histórico completo está disponível nas [releases do GitHub](https://github.com/crdcj/PYield/releases).
|
|
439
323
|
|
|
440
324
|
## Testes
|
|
441
325
|
|
|
@@ -281,136 +281,20 @@ yd.ptax("25-12-2025") # -> nan
|
|
|
281
281
|
|
|
282
282
|
Documentação completa: [crdcj.github.io/PYield](https://crdcj.github.io/PYield/)
|
|
283
283
|
|
|
284
|
-
##
|
|
285
|
-
|
|
286
|
-
Quebras acumuladas por versão desde a v0.52.0.
|
|
287
|
-
|
|
288
|
-
### v0.55.0
|
|
289
|
-
|
|
290
|
-
- `yd.lft.vna(...)`, `yd.lft.cotacao(...)`, `yd.lft.pu(...)`, `yd.ltn.pu(...)`,
|
|
291
|
-
`yd.ntnb.vna(...)`, `yd.ntnb.vna_projetado(...)`, `yd.ntnb.cotacao(...)`,
|
|
292
|
-
`yd.ntnb.pu(...)`, `yd.ntnbp.cotacao(...)`, `yd.ntnbp.pu(...)`,
|
|
293
|
-
`yd.ntnb1.cotacao(...)`, `yd.ntnb1.pu(...)`, `yd.ntnc.vna(...)`,
|
|
294
|
-
`yd.ntnc.vna_projetado(...)`, `yd.ntnc.cotacao(...)`, `yd.ntnc.pu(...)` e
|
|
295
|
-
`yd.ntnf.pu(...)` retornam `Decimal`, preservando exatamente os valores com
|
|
296
|
-
seis casas definidos pela fonte ou pela metodologia. Entradas numéricas
|
|
297
|
-
aceitam `float` ou `Decimal`.
|
|
298
|
-
|
|
299
|
-
### v0.54.5
|
|
300
|
-
|
|
301
|
-
- `yd.ntnf.fluxos_caixa(...)` não aceita mais `ajustar_datas_pagamento`. Os
|
|
302
|
-
cronogramas públicos de NTN-B, NTN-B1, NTN-C e NTN-F usam datas contratuais,
|
|
303
|
-
sem ajuste para dias úteis. Para obter datas efetivas de processamento, use
|
|
304
|
-
`yd.du.deslocar(..., 0)`.
|
|
305
|
-
|
|
306
|
-
### v0.54.2
|
|
307
|
-
|
|
308
|
-
- `yd.tpf.taxas_historicas(...)` foi adicionado para consultas por período ou
|
|
309
|
-
de todo o histórico disponível de taxas indicativas.
|
|
310
|
-
- `yd.tpf.taxas(..., completo=True)` foi removido. `yd.tpf.taxas(...)` mantém
|
|
311
|
-
a visão estável de TPF; para acessar todas as colunas processadas da fonte,
|
|
312
|
-
use `pyield.anbima.taxas.buscar(data)` ou `pyield.anbima.taxas.ler(fonte)`.
|
|
313
|
-
|
|
314
|
-
### v0.54.0
|
|
315
|
-
|
|
316
|
-
- LFT, NTN-B, NTN-C e NTN-B1 passaram a expor **cotação e valores de fluxo em
|
|
317
|
-
base 1** (fator decimal), em vez de base 100 (escala percentual da ANBIMA).
|
|
318
|
-
A precisão numérica é preservada: o truncamento de 4 casas na escala ANBIMA
|
|
319
|
-
equivale a 6 casas em base 1.
|
|
320
|
-
|
|
321
|
-
```python
|
|
322
|
-
# Antes
|
|
323
|
-
yd.ntnb.cotacao("31-05-2024", "15-05-2035", 0.061490) # -> 99.3651
|
|
324
|
-
yd.lft.pu(15785.324502, 99.9291) # -> 15774.132706
|
|
325
|
-
|
|
326
|
-
# Agora
|
|
327
|
-
yd.ntnb.cotacao("31-05-2024", "15-05-2035", 0.061490) # -> Decimal('0.993651')
|
|
328
|
-
yd.lft.pu(15785.324502, 0.999291) # -> Decimal('15774.132706')
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
Também afeta `fluxos_caixa(...)` de NTN-B e NTN-C: o cupom semestral passou
|
|
332
|
-
de `2,956301` para `0,02956301` (NTN-C 6% a.a.) e o pagamento final passou
|
|
333
|
-
de `102,956301` para `1,02956301`. Chamadas a `pu(vna, cotacao)` que
|
|
334
|
-
dividiam por 100 (`cotacao / 100`) devem passar diretamente o fator em
|
|
335
|
-
base 1. O PU final permanece na mesma escala financeira.
|
|
336
|
-
- `yd.ntnbprinc` (e `yd.tpf.ntnbprinc`) foi renomeado para `yd.ntnbp` (e
|
|
337
|
-
`yd.tpf.ntnbp`), alinhando o namespace público aos nomes curtos usados no
|
|
338
|
-
restante da biblioteca (`ntnb`, `ntnb1`, `ntnbp`, `ntnc`, `ntnf`).
|
|
339
|
-
- `yd.tpf.premio_pre` foi renomeado para `yd.tpf.premios_pre`, coerente com o
|
|
340
|
-
retorno tabular (`DataFrame` com múltiplos prêmios).
|
|
341
|
-
- Entradas escalares de data inválidas agora **levantam `ValueError`** em
|
|
342
|
-
qualquer função pública que aceite `DateLike`. Antes, chamadas como
|
|
343
|
-
`yd.du.deslocar("31-02-2024", 1)` retornavam `None` silenciosamente. Em
|
|
344
|
-
operações vetorizadas o parse continua tolerante: elementos inválidos viram
|
|
345
|
-
`null` para preservar o pipeline Polars. Strings vazias ou só com espaços
|
|
346
|
-
continuam sendo tratadas como nulas.
|
|
347
|
-
|
|
348
|
-
### v0.53.0
|
|
349
|
-
|
|
350
|
-
- As funções secundárias de TPF migraram do `yd.tpf` para o namespace dedicado
|
|
351
|
-
`yd.tpf.secundario`:
|
|
352
|
-
|
|
353
|
-
- `yd.tpf.secundario_intradia(...)` → `yd.tpf.secundario.intradia(...)`
|
|
354
|
-
- `yd.tpf.secundario_mensal(...)` → `yd.tpf.secundario.mensal(...)`
|
|
355
|
-
|
|
356
|
-
### v0.52.0
|
|
357
|
-
|
|
358
|
-
- `Interpolador(...)` e `Interpolador.interpolar(...)` agora aceitam **apenas
|
|
359
|
-
inteiro escalar**. Chamadas com lista/`pl.Series` (`interp([30, 60])`),
|
|
360
|
-
antes suportadas via sobrecarga vetorial, agora levantam `TypeError`.
|
|
361
|
-
Substitua por `Interpolador.interpolar_expr` em pipelines Polars ou pela
|
|
362
|
-
função top-level `yd.interpolar(...)` (curva única ou multi-curva).
|
|
363
|
-
- `yd.interpolar(...)` passou a usar `extrapolar=False` como padrão, alinhado
|
|
364
|
-
com `Interpolador`. Antes era `True`. Chamadas que dependiam da
|
|
365
|
-
extrapolação implícita na ponta longa precisam passar `extrapolar=True`
|
|
366
|
-
explicitamente.
|
|
367
|
-
- As funções `dv01(...)` dos títulos públicos agora recebem `pu` como argumento
|
|
368
|
-
explícito. Chamadas antigas que passavam apenas data, vencimento e taxa, ou
|
|
369
|
-
que passavam VNA no caso da NTN-B, precisam ser atualizadas.
|
|
370
|
-
- `ntnf.taxas_zero(...)` mudou os nomes dos parâmetros de curva para o padrão
|
|
371
|
-
`vencimentos_*` / `taxas_*`. Chamadas por posição continuam com a mesma
|
|
372
|
-
ordem; chamadas por keyword precisam usar os novos nomes.
|
|
373
|
-
|
|
374
|
-
#### `Interpolador(...)` não aceita mais lista/Series
|
|
375
|
-
|
|
376
|
-
O ``__call__`` e o método ``interpolar`` da classe agora são estritamente
|
|
377
|
-
escalares. O caminho vetorial foi dividido em duas APIs com semântica clara:
|
|
284
|
+
## Compatibilidade e mudanças da API
|
|
378
285
|
|
|
379
|
-
|
|
380
|
-
# Antes (v0.51.x)
|
|
381
|
-
interp = yd.Interpolador(dus, taxas, metodo="flat_forward")
|
|
382
|
-
interp([15, 45, 75]) # pl.Series
|
|
383
|
-
|
|
384
|
-
# Agora, dentro de um pipeline Polars
|
|
385
|
-
df.with_columns(taxa=interp.interpolar_expr("du"))
|
|
386
|
-
|
|
387
|
-
# Agora, ad-hoc (curva única ou multi-curva)
|
|
388
|
-
yd.interpolar(
|
|
389
|
-
dus_alvo=pl.Series([15, 45, 75]),
|
|
390
|
-
dus_curva=pl.Series(dus),
|
|
391
|
-
taxas_curva=pl.Series(taxas),
|
|
392
|
-
)
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
#### `dv01(...)` agora recebe PU
|
|
396
|
-
|
|
397
|
-
As funções `dv01(...)` dos títulos públicos recebem o PU usado como base para o
|
|
398
|
-
cálculo, não mais o VNA ou apenas a taxa. Se necessário, calcule o PU antes:
|
|
399
|
-
|
|
400
|
-
```python
|
|
401
|
-
pu = yd.ntnb.pu(vna, yd.ntnb.cotacao(data, vencimento, taxa))
|
|
402
|
-
dv01 = yd.ntnb.dv01(data, vencimento, taxa, pu)
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
#### `ntnf.taxas_zero(...)` usa nomes por conceito
|
|
286
|
+
A versão atual é `v0.55.0`. As mudanças abaixo exigem atualização de código:
|
|
406
287
|
|
|
407
|
-
|
|
408
|
-
|
|
288
|
+
| Versão | Mudança principal |
|
|
289
|
+
|---|---|
|
|
290
|
+
| `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
|
+
| `v0.54.5` | `fluxos_caixa` não aceita mais `ajustar_datas_pagamento`; os cronogramas usam datas contratuais. |
|
|
292
|
+
| `v0.54.2` | `taxas_historicas` foi adicionada e `tpf.taxas(completo=True)` foi removida. |
|
|
293
|
+
| `v0.54.0` | Cotações e fluxos de LFT, NTN-B, NTN-C e NTN-B1 passaram de base 100 para base 1; `ntnbprinc` virou `ntnbp`; `premio_pre` virou `premios_pre`; datas escalares inválidas passaram a levantar `ValueError`. |
|
|
294
|
+
| `v0.53.0` | O mercado secundário de TPF passou para `yd.tpf.secundario.intradia` e `yd.tpf.secundario.mensal`. |
|
|
295
|
+
| `v0.52.0` | `Interpolador` passou a ser escalar; use `interpolar_expr` ou `yd.interpolar` para vetores. `dv01` passou a exigir `pu` e `ntnf.taxas_zero` adotou nomes `vencimentos_*` / `taxas_*`. |
|
|
409
296
|
|
|
410
|
-
|
|
411
|
-
- `ltn_taxas` → `taxas_ltn`
|
|
412
|
-
- `ntnf_vencimentos` → `vencimentos_ntnf`
|
|
413
|
-
- `ntnf_taxas` → `taxas_ntnf`
|
|
297
|
+
O histórico completo está disponível nas [releases do GitHub](https://github.com/crdcj/PYield/releases).
|
|
414
298
|
|
|
415
299
|
## Testes
|
|
416
300
|
|
|
@@ -151,10 +151,10 @@ def calendar(
|
|
|
151
151
|
|
|
152
152
|
Examples:
|
|
153
153
|
>>> import pyield as yd
|
|
154
|
-
>>> cal = yd.selic.copom.calendar()
|
|
155
|
-
>>> "ExpiryDate" in cal.columns
|
|
154
|
+
>>> cal = yd.selic.copom.calendar() # doctest: +SKIP
|
|
155
|
+
>>> "ExpiryDate" in cal.columns # doctest: +SKIP
|
|
156
156
|
True
|
|
157
|
-
>>> cal["EndDate"].is_sorted()
|
|
157
|
+
>>> cal["EndDate"].is_sorted() # doctest: +SKIP
|
|
158
158
|
True
|
|
159
159
|
"""
|
|
160
160
|
past = _fetch_past_meetings()
|
|
@@ -192,8 +192,8 @@ def next_meeting(reference: DateLike | None = None) -> pl.DataFrame:
|
|
|
192
192
|
|
|
193
193
|
Examples:
|
|
194
194
|
>>> import pyield as yd
|
|
195
|
-
>>> row = yd.selic.copom.next_meeting()
|
|
196
|
-
>>> len(row)
|
|
195
|
+
>>> row = yd.selic.copom.next_meeting() # doctest: +SKIP
|
|
196
|
+
>>> len(row) # doctest: +SKIP
|
|
197
197
|
1
|
|
198
198
|
"""
|
|
199
199
|
ref = relogio.hoje() if reference is None else converter_datas(reference)
|
|
@@ -254,8 +254,8 @@ def data(date: DateLike) -> pl.DataFrame:
|
|
|
254
254
|
|
|
255
255
|
Examples:
|
|
256
256
|
>>> import pyield as yd
|
|
257
|
-
>>> df = yd.selic.cpm.data("29-01-2025")
|
|
258
|
-
>>> df.is_empty() or set(df.schema.keys()) >= {
|
|
257
|
+
>>> df = yd.selic.cpm.data("29-01-2025") # doctest: +SKIP
|
|
258
|
+
>>> df.is_empty() or set(df.schema.keys()) >= { # doctest: +SKIP
|
|
259
259
|
... "data_referencia",
|
|
260
260
|
... "codigo_negociacao",
|
|
261
261
|
... "data_fim_reuniao",
|
|
@@ -253,11 +253,13 @@ def all_meetings(
|
|
|
253
253
|
Examples:
|
|
254
254
|
>>> import pyield as yd
|
|
255
255
|
>>> import polars as pl
|
|
256
|
-
>>> df = yd.selic.probabilities.all_meetings("29-01-2025")
|
|
257
|
-
>>> df.is_empty() or df["ranking_reuniao"].min() == 1
|
|
256
|
+
>>> df = yd.selic.probabilities.all_meetings("29-01-2025") # doctest: +SKIP
|
|
257
|
+
>>> df.is_empty() or df["ranking_reuniao"].min() == 1 # doctest: +SKIP
|
|
258
258
|
True
|
|
259
|
-
>>> sums = df.group_by("data_expiracao").agg(
|
|
260
|
-
|
|
259
|
+
>>> sums = df.group_by("data_expiracao").agg(
|
|
260
|
+
... pl.col("prob").sum()
|
|
261
|
+
... ) # doctest: +SKIP
|
|
262
|
+
>>> df.is_empty() or (sums["prob"] - 1.0).abs().max() < 1e-9 # doctest: +SKIP
|
|
261
263
|
True
|
|
262
264
|
"""
|
|
263
265
|
raw = cpm.data(date)
|
|
@@ -318,10 +320,12 @@ def meeting(
|
|
|
318
320
|
|
|
319
321
|
Examples:
|
|
320
322
|
>>> import pyield as yd
|
|
321
|
-
>>> df = yd.selic.probabilities.meeting("29-01-2025")
|
|
322
|
-
>>> df.is_empty() or abs(df["prob"].sum() - 1.0) < 1e-9
|
|
323
|
+
>>> df = yd.selic.probabilities.meeting("29-01-2025") # doctest: +SKIP
|
|
324
|
+
>>> df.is_empty() or abs(df["prob"].sum() - 1.0) < 1e-9 # doctest: +SKIP
|
|
323
325
|
True
|
|
324
|
-
>>> df.is_empty() or df["prob_acumulada"].tail(
|
|
326
|
+
>>> df.is_empty() or df["prob_acumulada"].tail(
|
|
327
|
+
... 1
|
|
328
|
+
... ).item() == 1.0 # doctest: +SKIP
|
|
325
329
|
True
|
|
326
330
|
"""
|
|
327
331
|
df = all_meetings(date, option_type=option_type)
|
|
@@ -210,7 +210,8 @@ def taxas_zero(
|
|
|
210
210
|
Padrão False, retornando apenas os vencimentos informados.
|
|
211
211
|
|
|
212
212
|
Returns:
|
|
213
|
-
pl.DataFrame: Curva zero calibrada pelo método TD.
|
|
213
|
+
pl.DataFrame: Curva zero calibrada pelo método TD. Retorna vazio quando
|
|
214
|
+
não restarem vencimentos posteriores à liquidação.
|
|
214
215
|
|
|
215
216
|
Output Columns:
|
|
216
217
|
- data_vencimento (Date): Data do vértice da curva.
|
|
@@ -228,6 +229,16 @@ def taxas_zero(
|
|
|
228
229
|
liquidacao, vencimentos, taxas = _validar_entradas_taxas_zero(
|
|
229
230
|
data_liquidacao, vencimentos, taxas
|
|
230
231
|
)
|
|
232
|
+
if vencimentos.is_empty():
|
|
233
|
+
return pl.DataFrame(
|
|
234
|
+
schema={
|
|
235
|
+
"data_vencimento": pl.Date,
|
|
236
|
+
"dias_uteis": pl.Int64,
|
|
237
|
+
"taxa_zero": pl.Float64,
|
|
238
|
+
"taxa_forward": pl.Float64,
|
|
239
|
+
}
|
|
240
|
+
)
|
|
241
|
+
|
|
231
242
|
titulos = pl.DataFrame({"data_vencimento": vencimentos, "taxa_tir": taxas}).sort(
|
|
232
243
|
"data_vencimento"
|
|
233
244
|
)
|
|
@@ -122,6 +122,8 @@ def cotacao(
|
|
|
122
122
|
|
|
123
123
|
Returns:
|
|
124
124
|
Decimal: Fator de cotação em base 1, truncado em 6 casas decimais.
|
|
125
|
+
Retorna ``Decimal("NaN")`` quando o prazo até o vencimento não é
|
|
126
|
+
positivo.
|
|
125
127
|
|
|
126
128
|
Notes:
|
|
127
129
|
A STN apresenta a cotação na escala percentual (base 100). Esta
|
|
@@ -152,6 +154,8 @@ def cotacao(
|
|
|
152
154
|
taxa = utils.normalizar_taxa_precificacao(taxa)
|
|
153
155
|
# Número de dias úteis entre liquidação (inclusivo) e vencimento (exclusivo)
|
|
154
156
|
dias_uteis = du.contar(data_liquidacao, data_vencimento)
|
|
157
|
+
if dias_uteis <= 0:
|
|
158
|
+
return Decimal("NaN")
|
|
155
159
|
|
|
156
160
|
# Número de períodos truncado conforme regras da STN
|
|
157
161
|
anos_truncados = utils.truncar(dias_uteis / 252, 14)
|
|
@@ -201,6 +205,10 @@ def taxa(
|
|
|
201
205
|
if pu_float <= 0:
|
|
202
206
|
return float("nan")
|
|
203
207
|
|
|
208
|
+
dias_uteis = du.contar(data_liquidacao, data_vencimento)
|
|
209
|
+
if dias_uteis <= 0:
|
|
210
|
+
return float("nan")
|
|
211
|
+
|
|
204
212
|
def diferenca_preco(taxa: float) -> float:
|
|
205
213
|
preco = _calcular_pu(vna, cotacao(data_liquidacao, data_vencimento, taxa))
|
|
206
214
|
return float(preco) - pu_float
|
|
@@ -128,7 +128,8 @@ def pu(
|
|
|
128
128
|
taxa: Taxa de desconto (YTM) do título em formato decimal.
|
|
129
129
|
|
|
130
130
|
Returns:
|
|
131
|
-
Decimal: PU da LTN truncado em seis casas decimais.
|
|
131
|
+
Decimal: PU da LTN truncado em seis casas decimais. Retorna
|
|
132
|
+
``Decimal("NaN")`` quando o prazo até o vencimento não é positivo.
|
|
132
133
|
|
|
133
134
|
References:
|
|
134
135
|
- Secretaria do Tesouro Nacional. Metodologia de Cálculo dos Títulos
|
|
@@ -148,6 +149,8 @@ def pu(
|
|
|
148
149
|
taxa = utils.normalizar_taxa_precificacao(taxa)
|
|
149
150
|
# Calcula dias úteis entre liquidação e vencimento
|
|
150
151
|
dias_uteis = du.contar(data_liquidacao, data_vencimento)
|
|
152
|
+
if dias_uteis <= 0:
|
|
153
|
+
return Decimal("NaN")
|
|
151
154
|
|
|
152
155
|
# Calcula anos úteis truncados conforme a STN
|
|
153
156
|
anos_truncados = utils.truncar(dias_uteis / 252, 14)
|
|
@@ -196,6 +199,8 @@ def taxa(
|
|
|
196
199
|
return float("nan")
|
|
197
200
|
|
|
198
201
|
dias_uteis = du.contar(data_liquidacao, data_vencimento)
|
|
202
|
+
if dias_uteis <= 0:
|
|
203
|
+
return float("nan")
|
|
199
204
|
anos_truncados = utils.truncar(dias_uteis / 252, 14)
|
|
200
205
|
taxa_calculada = (VALOR_FACE / preco_float) ** (1 / anos_truncados) - 1
|
|
201
206
|
return utils.truncar(taxa_calculada, 8)
|
|
@@ -271,7 +276,8 @@ def dv01(
|
|
|
271
276
|
pu: PU usado como base para o cálculo.
|
|
272
277
|
|
|
273
278
|
Returns:
|
|
274
|
-
float: DV01, variação de preço para 1 bp.
|
|
279
|
+
float: DV01, variação de preço para 1 bp. Retorna ``NaN`` quando o
|
|
280
|
+
prazo até o vencimento não é positivo.
|
|
275
281
|
|
|
276
282
|
Examples:
|
|
277
283
|
>>> from pyield import ltn
|
|
@@ -285,6 +291,8 @@ def dv01(
|
|
|
285
291
|
taxa = utils.normalizar_taxa_precificacao(taxa)
|
|
286
292
|
taxa_mais_1bp = round(taxa + 0.0001, 8)
|
|
287
293
|
dias_uteis = du.contar(data_liquidacao, data_vencimento)
|
|
294
|
+
if dias_uteis <= 0:
|
|
295
|
+
return float("nan")
|
|
288
296
|
anos_truncados = utils.truncar(dias_uteis / 252, 14)
|
|
289
297
|
preco_1 = utils.truncar(VALOR_FACE / (1 + taxa) ** anos_truncados, 6)
|
|
290
298
|
preco_2 = utils.truncar(
|
|
@@ -308,7 +316,8 @@ def duration_expr(
|
|
|
308
316
|
Returns:
|
|
309
317
|
pl.Expr: Expressão sem alias com a duration em anos úteis.
|
|
310
318
|
"""
|
|
311
|
-
|
|
319
|
+
dias_uteis = du.contar_expr(data_liquidacao, data_vencimento)
|
|
320
|
+
return pl.when(dias_uteis > 0).then(dias_uteis / 252).otherwise(float("nan"))
|
|
312
321
|
|
|
313
322
|
|
|
314
323
|
def dv01_expr(
|
|
@@ -484,7 +484,8 @@ def taxas_zero(
|
|
|
484
484
|
Padrão False.
|
|
485
485
|
|
|
486
486
|
Returns:
|
|
487
|
-
pl.DataFrame: DataFrame com as taxas zero.
|
|
487
|
+
pl.DataFrame: DataFrame com as taxas zero. Retorna vazio quando não
|
|
488
|
+
restarem vencimentos posteriores à liquidação.
|
|
488
489
|
|
|
489
490
|
Output Columns:
|
|
490
491
|
- data_vencimento (Date): Data de vencimento.
|
|
@@ -561,6 +562,14 @@ def taxas_zero(
|
|
|
561
562
|
data_liquidacao, vencimentos, taxas = _validar_entradas_taxas_zero(
|
|
562
563
|
data_liquidacao, vencimentos, taxas
|
|
563
564
|
)
|
|
565
|
+
if vencimentos.is_empty():
|
|
566
|
+
return pl.DataFrame(
|
|
567
|
+
schema={
|
|
568
|
+
"data_vencimento": pl.Date,
|
|
569
|
+
"dias_uteis": pl.Int64,
|
|
570
|
+
"taxa_zero": pl.Float64,
|
|
571
|
+
}
|
|
572
|
+
)
|
|
564
573
|
|
|
565
574
|
df = _criar_df_bootstrap(data_liquidacao, taxas, vencimentos)
|
|
566
575
|
|
|
@@ -27,7 +27,8 @@ def cotacao(
|
|
|
27
27
|
taxa_tir: Taxa interna de retorno anualizada do título.
|
|
28
28
|
|
|
29
29
|
Returns:
|
|
30
|
-
Decimal: Cotação em base 1, truncada em 6 casas decimais.
|
|
30
|
+
Decimal: Cotação em base 1, truncada em 6 casas decimais. Retorna
|
|
31
|
+
``Decimal("NaN")`` se a liquidação for igual ou posterior ao vencimento.
|
|
31
32
|
|
|
32
33
|
Examples:
|
|
33
34
|
>>> from pyield import ntnbp
|
|
@@ -38,6 +39,8 @@ def cotacao(
|
|
|
38
39
|
return Decimal("NaN")
|
|
39
40
|
|
|
40
41
|
dias_uteis = du.contar(data_liquidacao, data_vencimento)
|
|
42
|
+
if dias_uteis <= 0:
|
|
43
|
+
return Decimal("NaN")
|
|
41
44
|
anos_uteis = utils.truncar(dias_uteis / 252, 14)
|
|
42
45
|
fator_desconto = 1 / (1 + float(taxa_tir)) ** anos_uteis
|
|
43
46
|
return truncar_decimal(fator_desconto, 6)
|
|
@@ -108,12 +111,16 @@ def taxa(
|
|
|
108
111
|
|
|
109
112
|
Returns:
|
|
110
113
|
float: TIR de mercado anualizada, arredondada em quatro casas decimais.
|
|
114
|
+
Retorna ``NaN`` se a liquidação for igual ou posterior ao vencimento.
|
|
111
115
|
"""
|
|
112
116
|
if any_is_empty(data_liquidacao, data_vencimento):
|
|
113
117
|
return float("nan")
|
|
114
118
|
|
|
115
|
-
curva = _normalizar_curva_zero(curva_zero)
|
|
116
119
|
dias_uteis = du.contar(data_liquidacao, data_vencimento)
|
|
120
|
+
if dias_uteis <= 0:
|
|
121
|
+
return float("nan")
|
|
122
|
+
|
|
123
|
+
curva = _normalizar_curva_zero(curva_zero)
|
|
117
124
|
taxa_zero = interpolador.Interpolador(
|
|
118
125
|
curva["dias_uteis"],
|
|
119
126
|
curva["taxa_zero"],
|
|
@@ -141,7 +148,8 @@ def dv01(
|
|
|
141
148
|
pu: PU usado como base para o cálculo.
|
|
142
149
|
|
|
143
150
|
Returns:
|
|
144
|
-
float: DV01 (Dollar Value of 01), variação de preço para 1 bp.
|
|
151
|
+
float: DV01 (Dollar Value of 01), variação de preço para 1 bp. Retorna
|
|
152
|
+
``NaN`` se a liquidação for igual ou posterior ao vencimento.
|
|
145
153
|
|
|
146
154
|
Examples:
|
|
147
155
|
>>> from pyield import ntnbp as bp
|
|
@@ -154,6 +162,8 @@ def dv01(
|
|
|
154
162
|
return float("nan")
|
|
155
163
|
|
|
156
164
|
dias_uteis = du.contar(data_liquidacao, data_vencimento)
|
|
165
|
+
if dias_uteis <= 0:
|
|
166
|
+
return float("nan")
|
|
157
167
|
anos_uteis = utils.truncar(dias_uteis / 252, 14)
|
|
158
168
|
fator_preco = (1 + taxa_tir) ** anos_uteis
|
|
159
169
|
fator_preco_1bp = (1 + taxa_tir + 0.0001) ** anos_uteis
|
|
@@ -346,6 +346,35 @@ def pu(
|
|
|
346
346
|
return truncar_decimal(_calcular_pu(data_liquidacao, data_vencimento, taxa), 6)
|
|
347
347
|
|
|
348
348
|
|
|
349
|
+
def _filtrar_vertices_futuros(
|
|
350
|
+
liquidacao: dt.date,
|
|
351
|
+
vencimentos: pl.Series,
|
|
352
|
+
taxas: pl.Series,
|
|
353
|
+
nome_curva: str,
|
|
354
|
+
) -> tuple[pl.Series, pl.Series]:
|
|
355
|
+
"""Remove vértices sem prazo preservando o alinhamento com as taxas."""
|
|
356
|
+
if len(vencimentos) != len(taxas):
|
|
357
|
+
raise ValueError(
|
|
358
|
+
f"Vencimentos e taxas de {nome_curva} devem ter o mesmo tamanho."
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
df = pl.DataFrame(
|
|
362
|
+
{"data_vencimento": vencimentos, "taxa": taxas},
|
|
363
|
+
schema={"data_vencimento": pl.Date, "taxa": pl.Float64},
|
|
364
|
+
).filter(pl.col("data_vencimento") > liquidacao)
|
|
365
|
+
|
|
366
|
+
removidos = len(vencimentos) - df.height
|
|
367
|
+
if removidos > 0:
|
|
368
|
+
logger.warning(
|
|
369
|
+
"Vencimentos de %s menores ou iguais à liquidação foram ignorados: "
|
|
370
|
+
"%s removidos.",
|
|
371
|
+
nome_curva,
|
|
372
|
+
removidos,
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
return df["data_vencimento"], df["taxa"]
|
|
376
|
+
|
|
377
|
+
|
|
349
378
|
def taxas_zero( # noqa
|
|
350
379
|
data_liquidacao: DateLike,
|
|
351
380
|
vencimentos_ltn: DatesLike,
|
|
@@ -385,6 +414,10 @@ def taxas_zero( # noqa
|
|
|
385
414
|
- dias_uteis (Int64): Dias úteis entre liquidação e vencimento.
|
|
386
415
|
- taxa_zero (Float64): Taxa zero (zero cupom).
|
|
387
416
|
|
|
417
|
+
Notes:
|
|
418
|
+
Vencimentos menores ou iguais à liquidação são ignorados antes da
|
|
419
|
+
construção dos interpoladores.
|
|
420
|
+
|
|
388
421
|
Examples:
|
|
389
422
|
>>> from pyield import ntnf, ltn
|
|
390
423
|
>>> df_ltn = ltn.dados("03-09-2024")
|
|
@@ -431,6 +464,27 @@ def taxas_zero( # noqa
|
|
|
431
464
|
else:
|
|
432
465
|
serie_ntnf_taxas = taxas_ntnf
|
|
433
466
|
|
|
467
|
+
vencimentos_ltn, serie_ltn_taxas = _filtrar_vertices_futuros(
|
|
468
|
+
liquidacao,
|
|
469
|
+
vencimentos_ltn,
|
|
470
|
+
serie_ltn_taxas,
|
|
471
|
+
"LTN",
|
|
472
|
+
)
|
|
473
|
+
vencimentos_ntnf, serie_ntnf_taxas = _filtrar_vertices_futuros(
|
|
474
|
+
liquidacao,
|
|
475
|
+
vencimentos_ntnf,
|
|
476
|
+
serie_ntnf_taxas,
|
|
477
|
+
"NTN-F",
|
|
478
|
+
)
|
|
479
|
+
if vencimentos_ltn.is_empty() or vencimentos_ntnf.is_empty():
|
|
480
|
+
return pl.DataFrame(
|
|
481
|
+
schema={
|
|
482
|
+
"data_vencimento": pl.Date,
|
|
483
|
+
"dias_uteis": pl.Int64,
|
|
484
|
+
"taxa_zero": pl.Float64,
|
|
485
|
+
}
|
|
486
|
+
)
|
|
487
|
+
|
|
434
488
|
# 2. Criar interpoladores (aceitam pl.Series diretamente)
|
|
435
489
|
interpolador_ltn = ip.Interpolador(
|
|
436
490
|
dias_uteis=du.contar(liquidacao, vencimentos_ltn),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Cálculos genéricos de VNA."""
|
|
2
2
|
|
|
3
3
|
import datetime as dt
|
|
4
4
|
|
|
@@ -20,7 +20,29 @@ def calcular_vna(
|
|
|
20
20
|
*,
|
|
21
21
|
fator_variacao: float | None = None,
|
|
22
22
|
) -> float:
|
|
23
|
-
"""Obtém o VNA publicado ou calcula o pró-rata entre pontos publicados.
|
|
23
|
+
"""Obtém o VNA publicado ou calcula o pró-rata entre pontos publicados.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
df: DataFrame com as colunas ``data`` (Date) e ``vna`` (Float64),
|
|
27
|
+
contendo no máximo um ponto publicado por data.
|
|
28
|
+
data: Data para a qual o VNA será obtido ou interpolado.
|
|
29
|
+
fator_variacao: Fator multiplicativo entre o ponto inicial e o final.
|
|
30
|
+
Se omitido, é calculado pela razão entre os VNAs dos dois pontos.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Float: VNA publicado ou calculado por pró-rata exponencial. Retorna
|
|
34
|
+
``float('nan')`` quando não houver pontos publicados dos dois lados da
|
|
35
|
+
data ou quando ela estiver fora do intervalo disponível.
|
|
36
|
+
|
|
37
|
+
Notes:
|
|
38
|
+
Um ponto publicado exatamente na data tem prioridade e ignora
|
|
39
|
+
``fator_variacao``. O expoente do pró-rata é truncado em catorze casas
|
|
40
|
+
e o resultado em seis casas.
|
|
41
|
+
|
|
42
|
+
Raises:
|
|
43
|
+
ValueError: Se o VNA-base não for positivo ou a variação for menor ou
|
|
44
|
+
igual a -100%.
|
|
45
|
+
"""
|
|
24
46
|
ponto_exato = df.filter(pl.col("data") == data)
|
|
25
47
|
if ponto_exato.height == 1:
|
|
26
48
|
return float(ponto_exato.item(0, "vna"))
|
|
@@ -73,3 +95,6 @@ def calcular_vna_projetado(
|
|
|
73
95
|
projecao_percentual / 100,
|
|
74
96
|
expoente,
|
|
75
97
|
)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
__all__ = ["calcular_vna"]
|
|
@@ -10,7 +10,8 @@ import pyield._internal.converters as conversores
|
|
|
10
10
|
from pyield._internal.numbers import truncar, truncar_decimal
|
|
11
11
|
from pyield._internal.types import DateLike, any_is_empty
|
|
12
12
|
from pyield.ipca import historico as _ipca
|
|
13
|
-
from pyield.tpf.vna import _download
|
|
13
|
+
from pyield.tpf.vna import _download
|
|
14
|
+
from pyield.tpf.vna import calculo as _vna
|
|
14
15
|
|
|
15
16
|
_DIA_INICIO_VIGENCIA = 15
|
|
16
17
|
_QTD_MARCOS_VIGENCIA = 2
|
|
@@ -23,7 +24,7 @@ def _processar(df_bruto: pl.DataFrame) -> pl.DataFrame:
|
|
|
23
24
|
"""Normaliza as duas colunas da planilha de NTN-B."""
|
|
24
25
|
return (
|
|
25
26
|
df_bruto.select(
|
|
26
|
-
data=
|
|
27
|
+
data=_vna.expressao_data(),
|
|
27
28
|
vna=pl.col("column_2").cast(pl.Float64, strict=False),
|
|
28
29
|
)
|
|
29
30
|
.filter(pl.col("data").is_not_null(), pl.col("vna").is_not_null())
|
|
@@ -94,7 +95,7 @@ def vna(data: DateLike | None = None) -> Decimal:
|
|
|
94
95
|
if math.isnan(fator_ipca):
|
|
95
96
|
return Decimal("NaN")
|
|
96
97
|
return truncar_decimal(
|
|
97
|
-
|
|
98
|
+
_vna.calcular_vna(
|
|
98
99
|
df,
|
|
99
100
|
data_convertida,
|
|
100
101
|
fator_variacao=fator_ipca,
|
|
@@ -174,12 +175,12 @@ def vna_projetado(
|
|
|
174
175
|
"""
|
|
175
176
|
if any_is_empty(data, vna_base, inflacao):
|
|
176
177
|
return Decimal("NaN")
|
|
177
|
-
if inflacao <=
|
|
178
|
+
if inflacao <= _vna.LIMITE_INFERIOR_PERCENTUAL:
|
|
178
179
|
raise ValueError("A inflação deve ser maior que -100%.")
|
|
179
180
|
data_convertida = conversores.converter_datas(data)
|
|
180
181
|
inicio, fim = _obter_vigencia(data_convertida)
|
|
181
182
|
expoente = (data_convertida - inicio).days / (fim - inicio).days
|
|
182
183
|
return truncar_decimal(
|
|
183
|
-
|
|
184
|
+
_vna.calcular_vna_projetado(float(vna_base), float(inflacao), expoente),
|
|
184
185
|
6,
|
|
185
186
|
)
|
|
@@ -8,7 +8,8 @@ import polars as pl
|
|
|
8
8
|
import pyield._internal.converters as conversores
|
|
9
9
|
from pyield._internal.numbers import truncar_decimal
|
|
10
10
|
from pyield._internal.types import DateLike, any_is_empty
|
|
11
|
-
from pyield.tpf.vna import _download
|
|
11
|
+
from pyield.tpf.vna import _download
|
|
12
|
+
from pyield.tpf.vna import calculo as _vna
|
|
12
13
|
|
|
13
14
|
_URL_PUBLICACAO = (
|
|
14
15
|
"https://www.tesourotransparente.gov.br/publicacoes/valor-nominal-de-ntn-c/"
|
|
@@ -24,7 +25,7 @@ def _processar(df_bruto: pl.DataFrame) -> pl.DataFrame:
|
|
|
24
25
|
series = []
|
|
25
26
|
for coluna, anos in _ANOS_VENCIMENTO.items():
|
|
26
27
|
serie = df_bruto.select(
|
|
27
|
-
data=
|
|
28
|
+
data=_vna.expressao_data(),
|
|
28
29
|
anos_vencimento=pl.lit(anos, dtype=pl.List(pl.Int64)),
|
|
29
30
|
vna=pl.col(coluna).cast(pl.Float64, strict=False),
|
|
30
31
|
).filter(
|
|
@@ -100,7 +101,7 @@ def vna(
|
|
|
100
101
|
df = vnas().filter(
|
|
101
102
|
pl.col("anos_vencimento").list.contains(vencimento_convertido.year)
|
|
102
103
|
)
|
|
103
|
-
return truncar_decimal(
|
|
104
|
+
return truncar_decimal(_vna.calcular_vna(df, data_convertida), 6)
|
|
104
105
|
|
|
105
106
|
|
|
106
107
|
def _obter_vigencia(data: dt.date) -> tuple[dt.date, dt.date]:
|
|
@@ -153,12 +154,12 @@ def vna_projetado(
|
|
|
153
154
|
"""
|
|
154
155
|
if any_is_empty(data, vna_base, inflacao):
|
|
155
156
|
return Decimal("NaN")
|
|
156
|
-
if inflacao <=
|
|
157
|
+
if inflacao <= _vna.LIMITE_INFERIOR_PERCENTUAL:
|
|
157
158
|
raise ValueError("A inflação deve ser maior que -100%.")
|
|
158
159
|
data_convertida = conversores.converter_datas(data)
|
|
159
160
|
inicio, fim = _obter_vigencia(data_convertida)
|
|
160
161
|
expoente = (data_convertida - inicio).days / (fim - inicio).days
|
|
161
162
|
return truncar_decimal(
|
|
162
|
-
|
|
163
|
+
_vna.calcular_vna_projetado(float(vna_base), float(inflacao), expoente),
|
|
163
164
|
6,
|
|
164
165
|
)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""Cálculos de VNA por tipo de título público."""
|
|
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
|
|
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
|