Pular para conteúdo

NTN-B

Cálculos e dados de NTN-B.

Convenções de precificação (STN, tabela 3): - Cotação em base 100, truncada a 4 casas. - VNA recebido no cálculo do PU: truncado a 6 casas. - Prazo de desconto: dias úteis / 252, truncado a 14 casas. - PU: truncado a 6 casas. - Cada fluxo descontado: arredondado a 10 casas.

Valores de referência derivados conforme as regras da STN, exibidos em base 100: TAXA_CUPOM = ((0.06 + 1) ** 0.5 - 1) * 100 # 6% a.a. com capitalização semestral VALOR_CUPOM = round(TAXA_CUPOM, 6) -> 2.956301 VALOR_FINAL = 102.956301 DIA_CUPOM = 15 MESES_CUPOM = {2, 5, 8, 11}

cotacao(data_liquidacao, data_vencimento, taxa)

Calcula a cotação da NTN-B pela metodologia da STN para leilões primários.

Parameters:

Name Type Description Default
data_liquidacao DateLike

Data de liquidação da operação.

required
data_vencimento DateLike

Data de vencimento da NTN-B.

required
taxa float | Decimal | str

TIR real ao ano, em decimal, na base de 252 dias úteis. Aceita também percentual explícito: "5.75%" ou "5,75%". Antes do cálculo, é truncada em oito casas decimais (seis na forma percentual), descartando as casas excedentes sem arredondar.

required

Returns:

Name Type Description
Decimal Decimal

Cotação em base 100, truncada em 4 casas. Retorna Decimal('NaN') em erro.

Notes

A cotação teórica em base 100 é:

\[ \mathrm{COT} = \sum_{i=1}^{n} \frac{\mathrm{FC}_i}{(1+y)^{t_i}} \]

onde:

  • \(\mathrm{FC}_i\): valor do pagamento \(i\) em base 100, incluindo cupom e amortização quando aplicável.
  • \(\mathrm{DU}_i\): dias úteis entre liquidação e pagamento \(i\).
  • \(t_i = \mathrm{DU}_i/252\): prazo até o pagamento \(i\), em anos de 252 dias úteis.
  • \(n\): número de pagamentos restantes.
  • \(y\): TIR real informada em taxa, em decimal, expressa ao ano na base de 252 dias úteis, relativa aos fluxos em base 100.

Os fluxos em base 100 representam percentuais do VNA, e não valores monetários futuros. O VNA incorpora a atualização pelo IPCA. No cálculo oficial, além do truncamento da TIR descrito em Args, cada prazo é truncado em 14 casas decimais.

A cotação é calculada e retornada na escala percentual (base 100), truncada em 4 casas conforme a STN. Por exemplo, 99.3651 representa 99,3651% do VNA; o PU é calculado como VNA * cotacao / 100.

O cupom semestral definido pela STN como 2,956301% é armazenado como 2,956301 em base 100, com 6 casas decimais. Cada fluxo descontado é arredondado em 10 casas antes da soma.

References
  • Secretaria do Tesouro Nacional. Metodologia de Cálculo dos Títulos Públicos Federais Ofertados nos Leilões Primários. https://crdcj.github.io/PYield/referencias/metodologia-calculo-tpf-stn/

Examples:

>>> from pyield import ntnb
>>> cotacao = ntnb.cotacao("31-05-2024", "15-05-2035", "6.149%")
>>> cotacao
Decimal('99.3651')
>>> ntnb.cotacao("31-05-2024", "15-08-2060", "6.1878%")
Decimal('99.5341')
>>> ntnb.cotacao("15-08-2024", "15-08-2032", "5.929%")
Decimal('100.6409')
>>> ntnb.cotacao("15-05-2024", "15-05-2025", "10%")
Decimal('96.4454')
>>> ntnb.cotacao("21-05-2008", "15-08-2010", "8.29%")
Decimal('97.0813')
Source code in pyield/tpf/titulos/ntnb.py
def cotacao(
    data_liquidacao: DateLike,
    data_vencimento: DateLike,
    taxa: float | Decimal | str,
) -> Decimal:
    r"""
    Calcula a cotação da NTN-B pela metodologia da STN para leilões primários.

    Args:
        data_liquidacao (DateLike): Data de liquidação da operação.
        data_vencimento (DateLike): Data de vencimento da NTN-B.
        taxa: TIR real ao ano, em decimal, na base de 252 dias úteis.
            Aceita também percentual explícito: "5.75%" ou "5,75%".
            Antes do cálculo, é truncada em oito casas decimais (seis na
            forma percentual), descartando as casas excedentes sem arredondar.

    Returns:
        Decimal: Cotação em base 100, truncada em 4 casas. Retorna
            Decimal('NaN') em erro.

    Notes:
        A cotação teórica em base 100 é:

        \[
        \mathrm{COT} = \sum_{i=1}^{n}
        \frac{\mathrm{FC}_i}{(1+y)^{t_i}}
        \]

        onde:

        - \(\mathrm{FC}_i\): valor do pagamento \(i\) em base 100,
          incluindo cupom e amortização quando aplicável.
        - \(\mathrm{DU}_i\): dias úteis entre liquidação e pagamento \(i\).
        - \(t_i = \mathrm{DU}_i/252\): prazo até o pagamento \(i\), em anos
          de 252 dias úteis.
        - \(n\): número de pagamentos restantes.
        - \(y\): TIR real informada em `taxa`, em decimal, expressa ao ano na
          base de 252 dias úteis, relativa aos fluxos em base 100.

        Os fluxos em base 100 representam percentuais do VNA, e não valores
        monetários futuros. O VNA incorpora a atualização pelo IPCA.
        No cálculo oficial, além do truncamento da TIR descrito em `Args`,
        cada prazo é truncado em 14 casas decimais.

        A cotação é calculada e retornada na escala percentual (base 100),
        truncada em 4 casas conforme a STN. Por exemplo, ``99.3651`` representa
        99,3651% do VNA; o PU é calculado como ``VNA * cotacao / 100``.

        O cupom semestral definido pela STN como 2,956301% é armazenado
        como 2,956301 em base 100, com 6 casas decimais. Cada fluxo
        descontado é arredondado em 10 casas antes da soma.

    References:
        - Secretaria do Tesouro Nacional. Metodologia de Cálculo dos Títulos
          Públicos Federais Ofertados nos Leilões Primários.
          https://crdcj.github.io/PYield/referencias/metodologia-calculo-tpf-stn/

    Examples:
        >>> from pyield import ntnb
        >>> cotacao = ntnb.cotacao("31-05-2024", "15-05-2035", "6.149%")
        >>> cotacao
        Decimal('99.3651')
        >>> ntnb.cotacao("31-05-2024", "15-08-2060", "6.1878%")
        Decimal('99.5341')
        >>> ntnb.cotacao("15-08-2024", "15-08-2032", "5.929%")
        Decimal('100.6409')
        >>> ntnb.cotacao("15-05-2024", "15-05-2025", "10%")
        Decimal('96.4454')
        >>> ntnb.cotacao("21-05-2008", "15-08-2010", "8.29%")
        Decimal('97.0813')
    """
    taxa = _utils.converter_taxa(taxa)
    if any_is_empty(data_liquidacao, data_vencimento, taxa):
        return Decimal("NaN")
    taxa = _utils.normalizar_taxa_precificacao(taxa)

    df_fluxos = fluxos_caixa(data_liquidacao, data_vencimento)
    if df_fluxos.is_empty():
        return Decimal("NaN")

    valores_fluxo = df_fluxos["valor_pagamento"]
    dias_uteis = du.contar(data_liquidacao, df_fluxos["data_pagamento"])
    anos_uteis = _utils.truncar(dias_uteis / 252, 14)
    fatores_desconto = (1 + taxa) ** anos_uteis
    # Calcula o valor presente de cada fluxo com arredondamento STN
    vp = (valores_fluxo / fatores_desconto).round(10)
    # Retorna a cotação (soma dos valores presentes) com truncamento STN
    # Soma decimal preserva os fluxos arredondados no limite do truncamento.
    cotacao_total = sum(Decimal(str(valor)) for valor in vp)
    return truncar_decimal(cotacao_total, 4)

dados(data)

Busca as taxas indicativas de NTN-B para a data de referência.

Parameters:

Name Type Description Default
data DateLike

Data da consulta.

required

Returns:

Type Description
DataFrame

pl.DataFrame: DataFrame Polars com os dados de NTN-B. Na ausência de dados, retorna vazio com as mesmas colunas e tipos.

Output Columns
  • data_referencia (Date): Data de referência dos dados.
  • titulo (String): Tipo do título (ex.: "NTN-B").
  • codigo_selic (Int64): Código do título no SELIC.
  • data_base (Date): Data base de emissão do título.
  • data_vencimento (Date): Data de vencimento do título.
  • dias_uteis (Int64): Dias úteis entre referência e vencimento.
  • duration (Float64): Macaulay Duration do título (anos).
  • prazo_medio (Float64): Prazo médio do título (anos).
  • dv01 (Float64): Variação no preço para 1bp de taxa.
  • pu (Float64): Preço unitário (PU).
  • taxa_compra (Float64): Taxa de compra (decimal).
  • taxa_venda (Float64): Taxa de venda (decimal).
  • taxa_indicativa (Float64): Taxa indicativa (decimal).
  • taxa_di (Float64): Taxa de ajuste do DI Futuro interpolada pelo método flat forward.
  • taxa_zero (Float64): Taxa zero real (via bootstrap das taxas indicativas).
  • taxa_forward (Float64): Taxa forward real (a partir das taxas zero).
  • inflacao_implicita (Float64): Inflação implícita (breakeven) calculada a partir de taxas nominais do DI Futuro e taxas zero das NTN-B.

Examples:

>>> from pyield import ntnb
>>> df_ntnb = ntnb.dados("23-08-2024")
Source code in pyield/tpf/titulos/ntnb.py
def dados(data: DateLike) -> pl.DataFrame:
    """
    Busca as taxas indicativas de NTN-B para a data de referência.

    Args:
        data (DateLike): Data da consulta.

    Returns:
        pl.DataFrame: DataFrame Polars com os dados de NTN-B. Na ausência de
            dados, retorna vazio com as mesmas colunas e tipos.

    Output Columns:
        - data_referencia (Date): Data de referência dos dados.
        - titulo (String): Tipo do título (ex.: "NTN-B").
        - codigo_selic (Int64): Código do título no SELIC.
        - data_base (Date): Data base de emissão do título.
        - data_vencimento (Date): Data de vencimento do título.
        - dias_uteis (Int64): Dias úteis entre referência e vencimento.
        - duration (Float64): Macaulay Duration do título (anos).
        - prazo_medio (Float64): Prazo médio do título (anos).
        - dv01 (Float64): Variação no preço para 1bp de taxa.
        - pu (Float64): Preço unitário (PU).
        - taxa_compra (Float64): Taxa de compra (decimal).
        - taxa_venda (Float64): Taxa de venda (decimal).
        - taxa_indicativa (Float64): Taxa indicativa (decimal).
        - taxa_di (Float64): Taxa de ajuste do DI Futuro interpolada pelo
            método flat forward.
        - taxa_zero (Float64): Taxa zero real (via bootstrap das taxas indicativas).
        - taxa_forward (Float64): Taxa forward real (a partir das taxas zero).
        - inflacao_implicita (Float64): Inflação implícita (breakeven) calculada
            a partir de taxas nominais do DI Futuro e taxas zero das NTN-B.

    Examples:
        >>> from pyield import ntnb
        >>> df_ntnb = ntnb.dados("23-08-2024")
    """
    from pyield.futuro import di1  # noqa: PLC0415

    df = _utils.obter_tpf(data, "NTN-B")
    if df.is_empty():
        return pl.DataFrame(schema=_SCHEMA_DADOS)

    # Adiciona duration, prazo_medio e dv01
    df = df.with_columns(
        dias_uteis=du.contar_expr("data_referencia", "data_vencimento"),
        duration=duration_expr("data_referencia", "data_vencimento", "taxa_indicativa"),
    ).with_columns(
        prazo_medio=pl.col("duration"),
        dv01=dv01_expr("data_referencia", "data_vencimento", "taxa_indicativa", "pu"),
    )

    # Busca curva DI bruta e calcula taxa_zero, taxa_di e inflação implícita
    df_di = di1.dados(data)
    df_bei = implicitas(
        data_liquidacao=data,
        vencimentos_tir=df["data_vencimento"],
        taxas_tir=df["taxa_indicativa"],
        vencimentos_nominais=df_di["data_vencimento"],
        taxas_nominais=df_di["taxa_ajuste"],
    ).select(
        pl.col("data_vencimento"),
        pl.col("taxa_zero_real").alias("taxa_zero"),
        pl.col("taxa_nominal").alias("taxa_di"),
        pl.col("inflacao_implicita"),
    )

    df = df.join(df_bei, on="data_vencimento", how="left")

    # Calcula taxas forward a partir das taxas zero
    df = df.with_columns(taxa_forward=fwd.forwards_expr("dias_uteis", "taxa_zero"))

    return df.select(*_SCHEMA_DADOS)

datas_pagamento(data_liquidacao, data_vencimento)

Gera todas as datas de pagamento entre liquidação e vencimento.

Os pagamentos são semestrais e seguem a série do título, com datas em 15/02, 15/05, 15/08 ou 15/11. No vencimento, o fluxo inclui o último cupom e a amortização do principal. As datas são contratuais e não são ajustadas para dias úteis.

Parameters:

Name Type Description Default
data_liquidacao DateLike

Data de liquidação (exclusiva).

required
data_vencimento DateLike

Data de vencimento.

required

Returns:

Type Description
Series

pl.Series: Série de datas de pagamento entre a liquidação (exclusiva) e o vencimento (inclusivo). Retorna série vazia se o vencimento for menor ou igual à liquidação.

Notes

Para obter as datas efetivas de processamento, use yd.du.deslocar(..., 0).

Examples:

>>> from pyield import ntnb
>>> ntnb.datas_pagamento("10-05-2024", "15-05-2025")
shape: (3,)
Series: 'datas_pagamento' [date]
[
    2024-05-15
    2024-11-15
    2025-05-15
]

A data de liquidação coincidente com um pagamento é exclusiva:

>>> ntnb.datas_pagamento("15-05-2024", "15-05-2025")
shape: (2,)
Series: 'datas_pagamento' [date]
[
    2024-11-15
    2025-05-15
]
Source code in pyield/tpf/titulos/ntnb.py
def datas_pagamento(
    data_liquidacao: DateLike,
    data_vencimento: DateLike,
) -> pl.Series:
    """
    Gera todas as datas de pagamento entre liquidação e vencimento.

    Os pagamentos são semestrais e seguem a série do título, com datas em
    15/02, 15/05, 15/08 ou 15/11. No vencimento, o fluxo inclui o último cupom
    e a amortização do principal. As datas são contratuais e não são ajustadas
    para dias úteis.

    Args:
        data_liquidacao (DateLike): Data de liquidação (exclusiva).
        data_vencimento (DateLike): Data de vencimento.

    Returns:
        pl.Series: Série de datas de pagamento entre a liquidação (exclusiva)
            e o vencimento (inclusivo). Retorna série vazia se o vencimento for
            menor ou igual à liquidação.

    Notes:
        Para obter as datas efetivas de processamento, use
        ``yd.du.deslocar(..., 0)``.

    Examples:
        >>> from pyield import ntnb
        >>> ntnb.datas_pagamento("10-05-2024", "15-05-2025")
        shape: (3,)
        Series: 'datas_pagamento' [date]
        [
            2024-05-15
            2024-11-15
            2025-05-15
        ]

        A data de liquidação coincidente com um pagamento é exclusiva:

        >>> ntnb.datas_pagamento("15-05-2024", "15-05-2025")
        shape: (2,)
        Series: 'datas_pagamento' [date]
        [
            2024-11-15
            2025-05-15
        ]
    """
    return _utils.gerar_datas_pagamento(data_liquidacao, data_vencimento)

duration(data_liquidacao, data_vencimento, taxa)

Calcula a Macaulay duration da NTN-B em anos úteis.

Parameters:

Name Type Description Default
data_liquidacao DateLike

Data de liquidação.

required
data_vencimento DateLike

Data de vencimento.

required
taxa float | str

TIR real ao ano, em decimal, na base de 252 dias úteis. Aceita também percentual explícito: "5.75%" ou "5,75%".

required

Returns:

Name Type Description
float float

Macaulay duration em anos úteis.

Notes

A Macaulay duration \(D\), em anos de 252 dias úteis, é:

\[ D = \frac{\sum_{i=1}^{n}t_i\,\mathrm{FC}_i/(1+y)^{t_i}} {\sum_{i=1}^{n}\mathrm{FC}_i/(1+y)^{t_i}} \]

onde:

  • \(\mathrm{FC}_i\): valor do pagamento \(i\) em base 100, incluindo cupom e amortização quando aplicável.
  • \(\mathrm{DU}_i\): dias úteis entre liquidação e pagamento \(i\).
  • \(t_i = \mathrm{DU}_i/252\): prazo até o pagamento \(i\), em anos de 252 dias úteis.
  • \(n\): número de pagamentos restantes.
  • \(y\): TIR real informada em taxa, em decimal, expressa ao ano na base de 252 dias úteis, relativa aos fluxos em base 100.

O desconto dos fluxos não aplica os arredondamentos e truncamentos da cotação oficial. O resultado é truncado em 14 casas decimais. O VNA, comum a todos os fluxos, cancela na razão.

Examples:

>>> from pyield import ntnb
>>> ntnb.duration("23-08-2024", "15-08-2060", "6.1005%")
15.08305431313046
Source code in pyield/tpf/titulos/ntnb.py
def duration(
    data_liquidacao: DateLike,
    data_vencimento: DateLike,
    taxa: float | str,
) -> float:
    r"""
    Calcula a Macaulay duration da NTN-B em anos úteis.

    Args:
        data_liquidacao (DateLike): Data de liquidação.
        data_vencimento (DateLike): Data de vencimento.
        taxa (float | str): TIR real ao ano, em decimal, na base de
            252 dias úteis.
            Aceita também percentual explícito: "5.75%" ou "5,75%".

    Returns:
        float: Macaulay duration em anos úteis.

    Notes:
        A Macaulay duration \(D\), em anos de 252 dias úteis, é:

        \[
        D = \frac{\sum_{i=1}^{n}t_i\,\mathrm{FC}_i/(1+y)^{t_i}}
        {\sum_{i=1}^{n}\mathrm{FC}_i/(1+y)^{t_i}}
        \]

        onde:

        - \(\mathrm{FC}_i\): valor do pagamento \(i\) em base 100,
          incluindo cupom e amortização quando aplicável.
        - \(\mathrm{DU}_i\): dias úteis entre liquidação e pagamento \(i\).
        - \(t_i = \mathrm{DU}_i/252\): prazo até o pagamento \(i\), em anos
          de 252 dias úteis.
        - \(n\): número de pagamentos restantes.
        - \(y\): TIR real informada em `taxa`, em decimal, expressa ao ano na
          base de 252 dias úteis, relativa aos fluxos em base 100.

        O desconto dos fluxos não aplica os arredondamentos e truncamentos
        da cotação oficial. O resultado é truncado em 14 casas decimais.
        O VNA, comum a todos os fluxos, cancela na razão.

    Examples:
        >>> from pyield import ntnb
        >>> ntnb.duration("23-08-2024", "15-08-2060", "6.1005%")
        15.08305431313046
    """
    if isinstance(taxa, str):
        taxa = float(_utils.converter_taxa(taxa))
    if any_is_empty(data_liquidacao, data_vencimento, taxa):
        return float("nan")

    df_fluxos = fluxos_caixa(data_liquidacao, data_vencimento)
    if df_fluxos.is_empty():
        return float("nan")

    anos_uteis = du.contar(data_liquidacao, df_fluxos["data_pagamento"]) / 252
    vp = df_fluxos["valor_pagamento"] / (1 + taxa) ** anos_uteis
    duration = float((vp * anos_uteis).sum()) / float(vp.sum())
    # Truncar para 14 casas decimais para repetibilidade dos resultados
    return _utils.truncar(duration, 14)

duration_expr(data_liquidacao, data_vencimento, taxa)

Cria expressão Polars para a duration da NTN-B.

O cálculo é aplicado linha a linha porque a duration depende dos fluxos de caixa do título.

Parameters:

Name Type Description Default
data_liquidacao Expr | str

Nome de coluna ou expressão Polars com a data de liquidação.

required
data_vencimento Expr | str

Nome de coluna ou expressão Polars com a data de vencimento.

required
taxa Expr | str

Nome de coluna ou expressão Polars com a TIR real ao ano, em decimal, na base de 252 dias úteis.

required

Returns:

Type Description
Expr

pl.Expr: Expressão sem alias com a Macaulay duration em anos úteis.

Source code in pyield/tpf/titulos/ntnb.py
def duration_expr(
    data_liquidacao: pl.Expr | str,
    data_vencimento: pl.Expr | str,
    taxa: pl.Expr | str,
) -> pl.Expr:
    """Cria expressão Polars para a duration da NTN-B.

    O cálculo é aplicado linha a linha porque a duration depende dos fluxos de
    caixa do título.

    Args:
        data_liquidacao: Nome de coluna ou expressão Polars com a data de
            liquidação.
        data_vencimento: Nome de coluna ou expressão Polars com a data de
            vencimento.
        taxa: Nome de coluna ou expressão Polars com a TIR real ao ano,
            em decimal, na base de 252 dias úteis.

    Returns:
        pl.Expr: Expressão sem alias com a Macaulay duration em anos úteis.
    """
    return pl.struct(
        _utils.coluna_ou_expr(data_liquidacao, "data_liquidacao"),
        _utils.coluna_ou_expr(data_vencimento, "data_vencimento"),
        _utils.coluna_ou_expr(taxa, "taxa"),
    ).map_elements(
        lambda s: duration(
            s["data_liquidacao"],
            s["data_vencimento"],
            s["taxa"],
        ),
        return_dtype=pl.Float64,
    )

dv01(data_liquidacao, data_vencimento, taxa, pu)

Calcula o DV01 (Dollar Value of 01) da NTN-B em R$.

Representa a variação do PU informado para um aumento de 1 bp (0,01 ponto percentual) na taxa.

Parameters:

Name Type Description Default
data_liquidacao DateLike

Data de liquidação.

required
data_vencimento DateLike

Data de vencimento.

required
taxa float | Decimal | str

TIR real ao ano, em decimal, na base de 252 dias úteis. Aceita também percentual explícito: "5.75%" ou "5,75%".

required
pu float | Decimal

PU usado como base para o cálculo.

required

Returns:

Name Type Description
float float

DV01, variação de preço para 1 bp.

Notes

Mantendo liquidação e vencimento fixos, os fluxos e seus prazos também ficam fixos. Nesse contexto, a cotação teórica em função da TIR é:

\[ \mathrm{COT}(y) = \sum_{i=1}^{n} \frac{\mathrm{FC}_i}{(1+y)^{t_i}} \]

O \(\mathrm{DV01}\) é a redução do preço, em R$, para um aumento de 1 bp na TIR, usando o PU informado como base:

\[ \mathrm{DV01} = \mathrm{PU}_{\mathrm{informado}} \left(1-\frac{\mathrm{COT}(y+0.0001)}{\mathrm{COT}(y)}\right) \]

onde:

  • \(\mathrm{FC}_i\): valor do pagamento \(i\) em base 100, incluindo cupom e amortização quando aplicável.
  • \(\mathrm{DU}_i\): dias úteis entre liquidação e pagamento \(i\).
  • \(t_i = \mathrm{DU}_i/252\): prazo até o pagamento \(i\), em anos de 252 dias úteis.
  • \(n\): número de pagamentos restantes.
  • \(y\): TIR real informada em taxa, em decimal, expressa ao ano na base de 252 dias úteis, relativa aos fluxos em base 100.
  • \(\mathrm{PU}_{\mathrm{informado}}\): preço recebido em pu.
  • \(0.0001\): aumento de 1 bp na TIR em decimal.

As cotações teóricas não aplicam os arredondamentos e truncamentos da cotação oficial. A razão entre elas é aplicada ao PU informado.

Examples:

>>> from pyield import ntnb
>>> cot = ntnb.cotacao("26-03-2025", "15-08-2060", "7.4358%")
>>> pu = ntnb.pu(4470.979474, cot)
>>> ntnb.dv01("26-03-2025", "15-08-2060", "7.4358%", pu)
4.638861271718656
Source code in pyield/tpf/titulos/ntnb.py
def dv01(
    data_liquidacao: DateLike,
    data_vencimento: DateLike,
    taxa: float | Decimal | str,
    pu: float | Decimal,
) -> float:
    r"""
    Calcula o DV01 (Dollar Value of 01) da NTN-B em R$.

    Representa a variação do PU informado para um aumento de 1 bp (0,01 ponto
    percentual) na taxa.

    Args:
        data_liquidacao (DateLike): Data de liquidação.
        data_vencimento (DateLike): Data de vencimento.
        taxa (float | Decimal | str): TIR real ao ano, em decimal,
            na base de 252 dias úteis.
            Aceita também percentual explícito: "5.75%" ou "5,75%".
        pu: PU usado como base para o cálculo.

    Returns:
        float: DV01, variação de preço para 1 bp.

    Notes:
        Mantendo liquidação e vencimento fixos, os fluxos e seus prazos
        também ficam fixos. Nesse contexto, a cotação teórica em função da
        TIR é:

        \[
        \mathrm{COT}(y) = \sum_{i=1}^{n}
        \frac{\mathrm{FC}_i}{(1+y)^{t_i}}
        \]

        O \(\mathrm{DV01}\) é a redução do preço, em R$, para um aumento
        de 1 bp na TIR, usando o PU informado como base:

        \[
        \mathrm{DV01} = \mathrm{PU}_{\mathrm{informado}}
        \left(1-\frac{\mathrm{COT}(y+0.0001)}{\mathrm{COT}(y)}\right)
        \]

        onde:

        - \(\mathrm{FC}_i\): valor do pagamento \(i\) em base 100,
          incluindo cupom e amortização quando aplicável.
        - \(\mathrm{DU}_i\): dias úteis entre liquidação e pagamento \(i\).
        - \(t_i = \mathrm{DU}_i/252\): prazo até o pagamento \(i\), em anos
          de 252 dias úteis.
        - \(n\): número de pagamentos restantes.
        - \(y\): TIR real informada em `taxa`, em decimal, expressa ao ano na
          base de 252 dias úteis, relativa aos fluxos em base 100.
        - \(\mathrm{PU}_{\mathrm{informado}}\): preço recebido em `pu`.
        - \(0.0001\): aumento de 1 bp na TIR em decimal.

        As cotações teóricas não aplicam os arredondamentos e truncamentos
        da cotação oficial. A razão entre elas é aplicada ao PU informado.

    Examples:
        >>> from pyield import ntnb
        >>> cot = ntnb.cotacao("26-03-2025", "15-08-2060", "7.4358%")
        >>> pu = ntnb.pu(4470.979474, cot)
        >>> ntnb.dv01("26-03-2025", "15-08-2060", "7.4358%", pu)
        4.638861271718656
    """
    taxa = _utils.converter_taxa(taxa)
    if any_is_empty(data_liquidacao, data_vencimento, taxa, pu):
        return float("nan")

    cot1 = _cotacao_analitica(data_liquidacao, data_vencimento, float(taxa))
    cot2 = _cotacao_analitica(data_liquidacao, data_vencimento, float(taxa) + 0.0001)
    return float(pu) * (1 - cot2 / cot1)

dv01_expr(data_liquidacao, data_vencimento, taxa, pu)

Cria expressão Polars para o DV01 da NTN-B.

O cálculo é aplicado linha a linha e reprifica o PU informado para um aumento de 1 bp na taxa.

Parameters:

Name Type Description Default
data_liquidacao Expr | str

Nome de coluna ou expressão Polars com a data de liquidação.

required
data_vencimento Expr | str

Nome de coluna ou expressão Polars com a data de vencimento.

required
taxa Expr | str

Nome de coluna ou expressão Polars com a TIR real ao ano, em decimal, na base de 252 dias úteis.

required
pu Expr | str

Nome de coluna ou expressão Polars com o PU usado como base.

required

Returns:

Type Description
Expr

pl.Expr: Expressão sem alias com o DV01.

Source code in pyield/tpf/titulos/ntnb.py
def dv01_expr(
    data_liquidacao: pl.Expr | str,
    data_vencimento: pl.Expr | str,
    taxa: pl.Expr | str,
    pu: pl.Expr | str,
) -> pl.Expr:
    """Cria expressão Polars para o DV01 da NTN-B.

    O cálculo é aplicado linha a linha e reprifica o PU informado para um
    aumento de 1 bp na taxa.

    Args:
        data_liquidacao: Nome de coluna ou expressão Polars com a data de
            liquidação.
        data_vencimento: Nome de coluna ou expressão Polars com a data de
            vencimento.
        taxa: Nome de coluna ou expressão Polars com a TIR real ao ano,
            em decimal, na base de 252 dias úteis.
        pu: Nome de coluna ou expressão Polars com o PU usado como base.

    Returns:
        pl.Expr: Expressão sem alias com o DV01.
    """
    return pl.struct(
        _utils.coluna_ou_expr(data_liquidacao, "data_liquidacao"),
        _utils.coluna_ou_expr(data_vencimento, "data_vencimento"),
        _utils.coluna_ou_expr(taxa, "taxa"),
        _utils.coluna_ou_expr(pu, "pu"),
    ).map_elements(
        lambda s: dv01(
            s["data_liquidacao"],
            s["data_vencimento"],
            s["taxa"],
            s["pu"],
        ),
        return_dtype=pl.Float64,
    )

fluxos_caixa(data_liquidacao, data_vencimento)

Gera os fluxos de caixa da NTN-B entre liquidação e vencimento.

Parameters:

Name Type Description Default
data_liquidacao DateLike

Data de liquidação (exclusiva).

required
data_vencimento DateLike

Data de vencimento.

required

Returns:

Type Description
DataFrame

pl.DataFrame: DataFrame com as colunas de fluxo.

Output Columns
  • data_pagamento (Date): Data contratual do pagamento, sem ajuste para dia útil.
  • valor_pagamento (Float64): Valor do pagamento em base 100.
Notes

Para obter as datas efetivas de processamento, use yd.du.deslocar(..., 0).

Examples:

>>> from pyield import ntnb
>>> with pl.Config(float_precision=6):
...     ntnb.fluxos_caixa("10-05-2024", "15-05-2025")
shape: (3, 2)
┌────────────────┬─────────────────┐
│ data_pagamento ┆ valor_pagamento │
│ ---            ┆ ---             │
│ date           ┆ f64             │
╞════════════════╪═════════════════╡
│ 2024-05-15     ┆ 2.956301        │
│ 2024-11-15     ┆ 2.956301        │
│ 2025-05-15     ┆ 102.956301      │
└────────────────┴─────────────────┘
Source code in pyield/tpf/titulos/ntnb.py
def fluxos_caixa(
    data_liquidacao: DateLike,
    data_vencimento: DateLike,
) -> pl.DataFrame:
    """
    Gera os fluxos de caixa da NTN-B entre liquidação e vencimento.

    Args:
        data_liquidacao (DateLike): Data de liquidação (exclusiva).
        data_vencimento (DateLike): Data de vencimento.

    Returns:
        pl.DataFrame: DataFrame com as colunas de fluxo.

    Output Columns:
        - data_pagamento (Date): Data contratual do pagamento, sem ajuste para
            dia útil.
        - valor_pagamento (Float64): Valor do pagamento em base 100.

    Notes:
        Para obter as datas efetivas de processamento, use
        ``yd.du.deslocar(..., 0)``.

    Examples:
        >>> from pyield import ntnb
        >>> with pl.Config(float_precision=6):
        ...     ntnb.fluxos_caixa("10-05-2024", "15-05-2025")
        shape: (3, 2)
        ┌────────────────┬─────────────────┐
        │ data_pagamento ┆ valor_pagamento │
        │ ---            ┆ ---             │
        │ date           ┆ f64             │
        ╞════════════════╪═════════════════╡
        │ 2024-05-15     ┆ 2.956301        │
        │ 2024-11-15     ┆ 2.956301        │
        │ 2025-05-15     ┆ 102.956301      │
        └────────────────┴─────────────────┘
    """
    if any_is_empty(data_liquidacao, data_vencimento):
        return pl.DataFrame(
            schema={"data_pagamento": pl.Date, "valor_pagamento": pl.Float64}
        )

    # Obtém as datas de pagamento entre liquidação e vencimento
    liquidacao = conversores.converter_datas(data_liquidacao)
    vencimento = conversores.converter_datas(data_vencimento)
    serie_datas_pagamento = datas_pagamento(liquidacao, vencimento)

    # Retorna DataFrame vazio se não houver pagamentos (liquidação >= vencimento)
    if serie_datas_pagamento.is_empty():
        return pl.DataFrame(
            schema={"data_pagamento": pl.Date, "valor_pagamento": pl.Float64}
        )

    df = pl.DataFrame(
        {"data_pagamento": serie_datas_pagamento},
    ).with_columns(
        pl.when(pl.col("data_pagamento") == vencimento)
        .then(VALOR_FINAL)
        .otherwise(VALOR_CUPOM)
        .alias("valor_pagamento")
    )

    return df

implicitas(data_liquidacao, vencimentos_tir, taxas_tir, vencimentos_nominais, taxas_nominais, *, extrapolar=False)

Calcula a inflação implícita para NTN-B contra uma curva nominal de referência.

A inflação implícita (breakeven) relaciona taxas zero reais e nominais, baseada nas taxas zero das NTN-B e na curva nominal informada.

Parameters:

Name Type Description Default
data_liquidacao DateLike

Data de liquidação da operação.

required
vencimentos_tir DatesLike

Vencimentos das NTN-B usadas como vértices da curva de TIR real.

required
taxas_tir ArrayLike

TIRs reais observadas das NTN-B correspondentes aos vencimentos informados. A função calcula a curva zero real a partir dessas taxas. São expressas ao ano, em decimal, na base de 252 dias úteis.

required
vencimentos_nominais DatesLike

Vencimentos da curva nominal de referência.

required
taxas_nominais ArrayLike

Taxas zero nominais ao ano, em decimal, na base de 252 dias úteis. A curva de referência pode representar DI Futuro, curva soberana prefixada ou outra curva nominal escolhida pelo usuário.

required
extrapolar bool

Se True, extrapola a curva nominal fora dos vencimentos informados. Se False, vencimentos fora do intervalo da curva nominal retornam valores nulos nas colunas dependentes dessa curva. O padrão é False.

False

Returns:

Type Description
DataFrame

pl.DataFrame: DataFrame com as taxas calculadas.

Output Columns
  • data_vencimento (Date): Data de vencimento.
  • dias_uteis (Int64): Dias úteis entre liquidação e vencimento.
  • taxa_tir_real (Float64): TIR real da NTN-B recebida na entrada, em formato decimal.
  • taxa_zero_real (Float64): Taxa real zero via bootstrap, em formato decimal.
  • taxa_nominal (Float64): Taxa nominal interpolada, em formato decimal.
  • inflacao_implicita (Float64): Inflação implícita (breakeven), em formato decimal.
Notes

A inflação implícita \(\pi(t)\), expressa ao ano e em decimal, é:

\[ \pi(t) = \frac{1+r_{\mathrm{nominal}}(t)} {1+r_{\mathrm{real}}(t)}-1 \]

onde:

  • \(\mathrm{DU}\): dias úteis entre liquidação e vencimento.
  • \(t = \mathrm{DU}/252\): prazo até o vencimento, em anos de 252 dias úteis.
  • \(r_{\mathrm{nominal}}(t)\): taxa zero nominal da curva fornecida, interpolada por flat_forward para o prazo \(t\).
  • \(r_{\mathrm{real}}(t)\): taxa zero real obtida pelo bootstrap das TIRs das NTN-B, para o mesmo prazo \(t\).

As taxas são expressas ao ano, em decimal, na base de 252 dias úteis. A comparação usa a taxa zero real, e não diretamente a TIR do título.

A inflação implícita é calculada contra a curva nominal informada. Se essa curva for DI Futuro, o resultado representa a implícita contra DI, não uma inflação soberana prefixada pura.

Examples:

Busca as taxas de NTN-B para uma data de referência. Estas são TIRs e as taxas zero são calculadas a partir delas.

>>> df_ntnb = yd.ntnb.dados("19-06-2026")

Busca as taxas de ajuste do DI Futuro para a mesma data de referência:

>>> df_di = yd.di1.dados("19-06-2026")

Calcula a inflação implícita na data de referência:

>>> curva = yd.ntnb.implicitas(
...     data_liquidacao="19-06-2026",
...     vencimentos_tir=df_ntnb["data_vencimento"],
...     taxas_tir=df_ntnb["taxa_indicativa"],
...     vencimentos_nominais=df_di["data_vencimento"],
...     taxas_nominais=df_di["taxa_ajuste"],
... )
>>> colunas_percentual = [
...     "taxa_tir_real",
...     "taxa_zero_real",
...     "taxa_nominal",
...     "inflacao_implicita",
... ]
>>> _ = pl.Config.set_tbl_width_chars(150)
>>> curva.with_columns(pl.col(colunas_percentual) * 100)
shape: (15, 6)
┌─────────────────┬────────────┬───────────────┬────────────────┬──────────────┬────────────────────┐
│ data_vencimento ┆ dias_uteis ┆ taxa_tir_real ┆ taxa_zero_real ┆ taxa_nominal ┆ inflacao_implicita │
│ ---             ┆ ---        ┆ ---           ┆ ---            ┆ ---          ┆ ---                │
│ date            ┆ i64        ┆ f64           ┆ f64            ┆ f64          ┆ f64                │
╞═════════════════╪════════════╪═══════════════╪════════════════╪══════════════╪════════════════════╡
│ 2026-08-15      ┆ 41         ┆ 11.15         ┆ 11.15          ┆ 14.133881    ┆ 2.684553           │
│ 2027-05-15      ┆ 226        ┆ 8.5733        ┆ 8.564222       ┆ 14.579493    ┆ 5.540749           │
│ 2028-08-15      ┆ 541        ┆ 8.9683        ┆ 8.970714       ┆ 14.914875    ┆ 5.454824           │
│ 2029-05-15      ┆ 725        ┆ 8.8171        ┆ 8.813162       ┆ 14.953463    ┆ 5.642977           │
│ 2030-08-15      ┆ 1039       ┆ 8.8766        ┆ 8.875555       ┆ 14.916618    ┆ 5.548594           │
│ …               ┆ …          ┆ …             ┆ …              ┆ …            ┆ …                  │
│ 2040-08-15      ┆ 3548       ┆ 7.8262        ┆ 7.608037       ┆ 14.591       ┆ 6.489258           │
│ 2045-05-15      ┆ 4735       ┆ 7.6656        ┆ 7.394303       ┆ null         ┆ null               │
│ 2050-08-15      ┆ 6050       ┆ 7.5659        ┆ 7.243255       ┆ null         ┆ null               │
│ 2055-05-15      ┆ 7239       ┆ 7.4658        ┆ 7.05134        ┆ null         ┆ null               │
│ 2060-08-15      ┆ 8556       ┆ 7.464         ┆ 7.082527       ┆ null         ┆ null               │
└─────────────────┴────────────┴───────────────┴────────────────┴──────────────┴────────────────────┘
Source code in pyield/tpf/titulos/ntnb.py
def implicitas(  # noqa: PLR0913
    data_liquidacao: DateLike,
    vencimentos_tir: DatesLike,
    taxas_tir: ArrayLike,
    vencimentos_nominais: DatesLike,
    taxas_nominais: ArrayLike,
    *,
    extrapolar: bool = False,
) -> pl.DataFrame:
    r"""
    Calcula a inflação implícita para NTN-B contra uma curva nominal de referência.

    A inflação implícita (breakeven) relaciona taxas zero reais e nominais,
    baseada nas taxas zero das NTN-B e na curva nominal informada.

    Args:
        data_liquidacao (DateLike): Data de liquidação da operação.
        vencimentos_tir (DatesLike): Vencimentos das NTN-B usadas como vértices
            da curva de TIR real.
        taxas_tir (ArrayLike): TIRs reais observadas das NTN-B correspondentes
            aos vencimentos informados. A função calcula a curva zero real a
            partir dessas taxas. São expressas ao ano, em decimal, na base
            de 252 dias úteis.
        vencimentos_nominais (DatesLike): Vencimentos da curva nominal de
            referência.
        taxas_nominais (ArrayLike): Taxas zero nominais ao ano, em
            decimal, na base de 252 dias úteis. A curva de referência pode
            representar DI Futuro, curva soberana prefixada ou outra curva
            nominal escolhida pelo usuário.
        extrapolar (bool): Se `True`, extrapola a curva nominal fora dos
            vencimentos informados. Se `False`, vencimentos fora do intervalo da
            curva nominal retornam valores nulos nas colunas dependentes dessa
            curva. O padrão é `False`.

    Returns:
        pl.DataFrame: DataFrame com as taxas calculadas.

    Output Columns:
        - data_vencimento (Date): Data de vencimento.
        - dias_uteis (Int64): Dias úteis entre liquidação e vencimento.
        - taxa_tir_real (Float64): TIR real da NTN-B recebida na entrada, em
            formato decimal.
        - taxa_zero_real (Float64): Taxa real zero via bootstrap, em formato
            decimal.
        - taxa_nominal (Float64): Taxa nominal interpolada, em formato decimal.
        - inflacao_implicita (Float64): Inflação implícita (breakeven), em
            formato decimal.

    Notes:
        A inflação implícita \(\pi(t)\), expressa ao ano e em decimal, é:

        \[
        \pi(t) = \frac{1+r_{\mathrm{nominal}}(t)}
        {1+r_{\mathrm{real}}(t)}-1
        \]

        onde:

        - \(\mathrm{DU}\): dias úteis entre liquidação e vencimento.
        - \(t = \mathrm{DU}/252\): prazo até o vencimento, em anos de
          252 dias úteis.
        - \(r_{\mathrm{nominal}}(t)\): taxa zero nominal da curva fornecida,
          interpolada por `flat_forward` para o prazo \(t\).
        - \(r_{\mathrm{real}}(t)\): taxa zero real obtida pelo bootstrap das
          TIRs das NTN-B, para o mesmo prazo \(t\).

        As taxas são expressas ao ano, em decimal, na base de 252 dias úteis.
        A comparação usa a taxa zero real, e não diretamente a TIR do título.

        A inflação implícita é calculada contra a curva nominal informada. Se
        essa curva for DI Futuro, o resultado representa a implícita contra DI,
        não uma inflação soberana prefixada pura.

    Examples:
        Busca as taxas de NTN-B para uma data de referência.
        Estas são TIRs e as taxas zero são calculadas a partir delas.
        >>> df_ntnb = yd.ntnb.dados("19-06-2026")

        Busca as taxas de ajuste do DI Futuro para a mesma data de referência:
        >>> df_di = yd.di1.dados("19-06-2026")

        Calcula a inflação implícita na data de referência:
        >>> curva = yd.ntnb.implicitas(
        ...     data_liquidacao="19-06-2026",
        ...     vencimentos_tir=df_ntnb["data_vencimento"],
        ...     taxas_tir=df_ntnb["taxa_indicativa"],
        ...     vencimentos_nominais=df_di["data_vencimento"],
        ...     taxas_nominais=df_di["taxa_ajuste"],
        ... )
        >>> colunas_percentual = [
        ...     "taxa_tir_real",
        ...     "taxa_zero_real",
        ...     "taxa_nominal",
        ...     "inflacao_implicita",
        ... ]
        >>> _ = pl.Config.set_tbl_width_chars(150)
        >>> curva.with_columns(pl.col(colunas_percentual) * 100)
        shape: (15, 6)
        ┌─────────────────┬────────────┬───────────────┬────────────────┬──────────────┬────────────────────┐
        │ data_vencimento ┆ dias_uteis ┆ taxa_tir_real ┆ taxa_zero_real ┆ taxa_nominal ┆ inflacao_implicita │
        │ ---             ┆ ---        ┆ ---           ┆ ---            ┆ ---          ┆ ---                │
        │ date            ┆ i64        ┆ f64           ┆ f64            ┆ f64          ┆ f64                │
        ╞═════════════════╪════════════╪═══════════════╪════════════════╪══════════════╪════════════════════╡
        │ 2026-08-15      ┆ 41         ┆ 11.15         ┆ 11.15          ┆ 14.133881    ┆ 2.684553           │
        │ 2027-05-15      ┆ 226        ┆ 8.5733        ┆ 8.564222       ┆ 14.579493    ┆ 5.540749           │
        │ 2028-08-15      ┆ 541        ┆ 8.9683        ┆ 8.970714       ┆ 14.914875    ┆ 5.454824           │
        │ 2029-05-15      ┆ 725        ┆ 8.8171        ┆ 8.813162       ┆ 14.953463    ┆ 5.642977           │
        │ 2030-08-15      ┆ 1039       ┆ 8.8766        ┆ 8.875555       ┆ 14.916618    ┆ 5.548594           │
        │ …               ┆ …          ┆ …             ┆ …              ┆ …            ┆ …                  │
        │ 2040-08-15      ┆ 3548       ┆ 7.8262        ┆ 7.608037       ┆ 14.591       ┆ 6.489258           │
        │ 2045-05-15      ┆ 4735       ┆ 7.6656        ┆ 7.394303       ┆ null         ┆ null               │
        │ 2050-08-15      ┆ 6050       ┆ 7.5659        ┆ 7.243255       ┆ null         ┆ null               │
        │ 2055-05-15      ┆ 7239       ┆ 7.4658        ┆ 7.05134        ┆ null         ┆ null               │
        │ 2060-08-15      ┆ 8556       ┆ 7.464         ┆ 7.082527       ┆ null         ┆ null               │
        └─────────────────┴────────────┴───────────────┴────────────────┴──────────────┴────────────────────┘
    """
    if any_is_empty(
        data_liquidacao,
        vencimentos_tir,
        taxas_tir,
        vencimentos_nominais,
        taxas_nominais,
    ):
        return pl.DataFrame()
    liquidacao, vencimentos_tir, taxas_tir = _validar_entradas_taxas_zero(
        data_liquidacao, vencimentos_tir, taxas_tir
    )
    vencimentos_nominais = conversores.converter_datas(vencimentos_nominais)

    interpolador_ff = interpolador.Interpolador(
        dias_uteis=du.contar(liquidacao, vencimentos_nominais),
        taxas=taxas_nominais,
        metodo="flat_forward",
        extrapolar=extrapolar,
    )
    taxa_nominal_expr = interpolador_ff.interpolar_expr("dias_uteis")
    df = taxas_zero(liquidacao, vencimentos_tir, taxas_tir).select(
        "data_vencimento",
        "dias_uteis",
        taxa_tir_real=pl.col("taxa_tir"),
        taxa_zero_real=pl.col("taxa_zero"),
        taxa_nominal=taxa_nominal_expr,
        inflacao_implicita=(taxa_nominal_expr + 1) / (pl.col("taxa_zero") + 1) - 1,
    )

    return df

pu(vna, cotacao)

Calcula o PU da NTN-B pela metodologia da STN para leilões primários.

Parameters:

Name Type Description Default
vna float | Decimal

Valor nominal atualizado (VNA), truncado em seis casas decimais antes do cálculo, sem arredondar.

required
cotacao float | Decimal

Cotação da NTN-B em base 100. Truncada em quatro casas decimais antes do cálculo, sem arredondar.

required

Returns:

Name Type Description
Decimal Decimal

Preço da NTN-B truncado em 6 casas decimais.

Notes

O preço unitário é:

\[ \mathrm{PU} = \mathrm{VNA}\,\frac{\mathrm{COT}}{100} \]

onde:

  • \(\mathrm{VNA}\): valor nominal atualizado pelo IPCA, recebido em vna.
  • \(\mathrm{COT}\): cotação recebida em cotacao, em base 100.

O cálculo aplica os truncamentos de VNA e cotação descritos em Args e trunca o PU em seis casas decimais.

References
  • Secretaria do Tesouro Nacional. Metodologia de Cálculo dos Títulos Públicos Federais Ofertados nos Leilões Primários. https://crdcj.github.io/PYield/referencias/metodologia-calculo-tpf-stn/

Examples:

>>> from pyield import ntnb
>>> ntnb.pu(4299.160173, 99.3651)
Decimal('4271.864805')
>>> ntnb.pu(4315.498383, 100.6409)
Decimal('4343.156412')
Source code in pyield/tpf/titulos/ntnb.py
def pu(
    vna: float | Decimal,
    cotacao: float | Decimal,
) -> Decimal:
    r"""
    Calcula o PU da NTN-B pela metodologia da STN para leilões primários.

    Args:
        vna: Valor nominal atualizado (VNA), truncado em seis casas decimais
            antes do cálculo, sem arredondar.
        cotacao: Cotação da NTN-B em base 100.
            Truncada em quatro casas decimais antes do cálculo, sem arredondar.

    Returns:
        Decimal: Preço da NTN-B truncado em 6 casas decimais.

    Notes:
        O preço unitário é:

        \[
        \mathrm{PU} = \mathrm{VNA}\,\frac{\mathrm{COT}}{100}
        \]

        onde:

        - \(\mathrm{VNA}\): valor nominal atualizado pelo IPCA, recebido
          em `vna`.
        - \(\mathrm{COT}\): cotação recebida em `cotacao`, em base 100.

        O cálculo aplica os truncamentos de VNA e cotação descritos em `Args`
        e trunca o PU em seis casas decimais.

    References:
        - Secretaria do Tesouro Nacional. Metodologia de Cálculo dos Títulos
          Públicos Federais Ofertados nos Leilões Primários.
          https://crdcj.github.io/PYield/referencias/metodologia-calculo-tpf-stn/

    Examples:
        >>> from pyield import ntnb
        >>> ntnb.pu(4299.160173, 99.3651)
        Decimal('4271.864805')
        >>> ntnb.pu(4315.498383, 100.6409)
        Decimal('4343.156412')
    """
    return _calcular_pu(vna, cotacao)

taxa(data_liquidacao, data_vencimento, vna, pu)

Calcula a TIR implícita de uma NTN-B a partir do preço (PU).

A função resolve numericamente a taxa que aproxima o PU informado por VNA * cotação / 100, com a cotação calculada pelo valor presente bruto dos fluxos. Como não há arredondamento nem truncamento intermediário, o resultado não pretende reproduzir exatamente a taxa usada para gerar um PU canônico por pu(vna, cotacao(...)).

Parameters:

Name Type Description Default
data_liquidacao DateLike

Data de liquidação.

required
data_vencimento DateLike

Data de vencimento.

required
vna float | Decimal

Valor nominal atualizado (VNA).

required
pu float | Decimal

Preço unitário (PU) do título.

required

Returns:

Name Type Description
float float

TIR implícita em formato decimal, sem arredondamento. Retorna NaN para entradas ausentes, PU não positivo ou falha na resolução numérica.

Notes

A TIR real implícita \(y\), expressa ao ano, em decimal e na base de 252 dias úteis, é encontrada resolvendo:

\[ \frac{\mathrm{VNA}}{100} \sum_{i=1}^{n}\frac{\mathrm{FC}_i}{(1+y)^{t_i}} = \mathrm{PU}_{\mathrm{informado}} \]

onde:

  • \(\mathrm{FC}_i\): valor do pagamento \(i\) em base 100, incluindo cupom e amortização quando aplicável.
  • \(\mathrm{DU}_i\): dias úteis entre liquidação e pagamento \(i\).
  • \(t_i = \mathrm{DU}_i/252\): prazo até o pagamento \(i\), em anos de 252 dias úteis.
  • \(n\): número de pagamentos restantes.
  • \(\mathrm{VNA}\): valor nominal atualizado pelo IPCA, recebido em vna.
  • \(\mathrm{PU}_{\mathrm{informado}}\): preço recebido em pu.

O lado esquerdo representa o preço teórico, sem os arredondamentos e truncamentos do PU oficial. A TIR se refere aos fluxos em base 100; a atualização pelo IPCA é representada pelo VNA.

Examples:

Exibe as taxas em formato decimal:

>>> from pyield import ntnb
>>> ntnb.taxa("31-05-2024", "15-05-2035", 4299.160173, 4271.864805)
0.06149006550833583
>>> ntnb.taxa("15-08-2024", "15-08-2032", 4315.498383, 4343.156412) * 100
5.929006687775254
>>> ntnb.taxa("21-05-2008", "15-08-2010", 1728.461136, 1781.867128) * 100
5.234584951922298
Source code in pyield/tpf/titulos/ntnb.py
def taxa(
    data_liquidacao: DateLike,
    data_vencimento: DateLike,
    vna: float | Decimal,
    pu: float | Decimal,
) -> float:
    r"""
    Calcula a TIR implícita de uma NTN-B a partir do preço (PU).

    A função resolve numericamente a taxa que aproxima o PU informado por
    ``VNA * cotação / 100``, com a cotação calculada pelo valor presente bruto
    dos fluxos. Como não há arredondamento nem truncamento intermediário, o
    resultado não pretende reproduzir exatamente a taxa usada para gerar um PU
    canônico por ``pu(vna, cotacao(...))``.

    Args:
        data_liquidacao (DateLike): Data de liquidação.
        data_vencimento (DateLike): Data de vencimento.
        vna: Valor nominal atualizado (VNA).
        pu: Preço unitário (PU) do título.

    Returns:
        float: TIR implícita em formato decimal, sem arredondamento. Retorna
            NaN para entradas ausentes, PU não positivo ou falha na resolução
            numérica.

    Notes:
        A TIR real implícita \(y\), expressa ao ano, em decimal e na base de
        252 dias úteis, é encontrada resolvendo:

        \[
        \frac{\mathrm{VNA}}{100}
        \sum_{i=1}^{n}\frac{\mathrm{FC}_i}{(1+y)^{t_i}}
        = \mathrm{PU}_{\mathrm{informado}}
        \]

        onde:

        - \(\mathrm{FC}_i\): valor do pagamento \(i\) em base 100,
          incluindo cupom e amortização quando aplicável.
        - \(\mathrm{DU}_i\): dias úteis entre liquidação e pagamento \(i\).
        - \(t_i = \mathrm{DU}_i/252\): prazo até o pagamento \(i\), em anos
          de 252 dias úteis.
        - \(n\): número de pagamentos restantes.
        - \(\mathrm{VNA}\): valor nominal atualizado pelo IPCA, recebido
          em `vna`.
        - \(\mathrm{PU}_{\mathrm{informado}}\): preço recebido em `pu`.

        O lado esquerdo representa o preço teórico, sem os arredondamentos
        e truncamentos do PU oficial. A TIR se refere aos fluxos em base 100;
        a atualização pelo IPCA é representada pelo VNA.

    Examples:
        Exibe as taxas em formato decimal:

        >>> from pyield import ntnb
        >>> ntnb.taxa("31-05-2024", "15-05-2035", 4299.160173, 4271.864805)
        0.06149006550833583
        >>> ntnb.taxa("15-08-2024", "15-08-2032", 4315.498383, 4343.156412) * 100
        5.929006687775254
        >>> ntnb.taxa("21-05-2008", "15-08-2010", 1728.461136, 1781.867128) * 100
        5.234584951922298
    """
    if any_is_empty(data_liquidacao, data_vencimento, vna, pu):
        return float("nan")

    pu_float = float(pu)
    if pu_float <= 0:
        return float("nan")
    vna_float = float(vna)

    def diferenca_preco(taxa_encontrada: float) -> float:
        cot = _cotacao_analitica(data_liquidacao, data_vencimento, taxa_encontrada)
        return vna_float * cot / 100 - pu_float

    return _utils.encontrar_raiz(diferenca_preco)

taxas_zero(data_liquidacao, vencimentos, taxas)

Calcula a curva zero de NTN-B pelo bootstrap de forwards.

Retorna apenas os vencimentos informados, com taxas em base 252 dias úteis. As entradas podem ser as taxas indicativas da ANBIMA obtidas por yd.ntnb.dados ou TIRs fornecidas pelo usuário.

O método calcula as taxas zero a partir das TIRs observadas das NTN-B, calibrando uma taxa forward por trecho para que os fluxos descontados pela curva zero reproduzam a cotação obtida pela TIR de cada título. A calibração é iterativa e tem convergência condicional: depende da existência de um intervalo válido para cada raiz.

Notes

Racional

Uma TIR desconta todos os fluxos de um título por uma única taxa. Uma curva zero, por outro lado, precisa de uma taxa para cada data de fluxo. O bootstrap transforma as TIRs em uma estrutura de taxas forward e, a partir dela, em taxas zero.

Curva de forwards e taxa zero

A curva usa como vértices as datas de pagamento dos títulos informados. Para cada NTN-B, a taxa forward calibrada é mantida constante desde o vencimento anterior até o seu vencimento. A primeira taxa forward começa na TIR do título mais curto.

A taxa zero é obtida acumulando os fatores dos trechos calibrados:

\[ r_0 = f_0 \]
\[ (1 + r_i)^{t_i} = (1 + r_{i-1})^{t_{i-1}} (1 + f_i)^{t_i - t_{i-1}} \]

onde:

  • \(i\): índice dos títulos em ordem de vencimento, começando em zero.
  • \(\mathrm{DU}_i\): dias úteis entre liquidação e vencimento do título \(i\).
  • \(t_i = \mathrm{DU}_i/252\): prazo até o vencimento do título \(i\), em anos de 252 dias úteis.
  • \(f_i\): taxa forward real do trecho que termina no vencimento do título \(i\). O primeiro trecho começa na liquidação.
  • \(r_i\): taxa zero real para o prazo \(t_i\).

As taxas são expressas ao ano, em decimal, na base de 252 dias úteis.

Calibração sequencial

Para cada título, do menor para o maior vencimento, a função calcula a cotação-alvo pela TIR observada:

\[ \mathrm{COT}_i^{\mathrm{TIR}} = \sum_{k=1}^{n_i} \frac{\mathrm{FC}_{i,k}}{(1+y_i)^{t_{i,k}}} \]

Em seguida, busca por bisseção o forward \(f_i\) que zera o erro de cotação \(E_i(f_i)\):

\[ E_i(f_i) = \sum_{k=1}^{n_i} \frac{\mathrm{FC}_{i,k}}{(1 + r(t_{i,k}; f_i))^{t_{i,k}}} - \mathrm{COT}_i^{\mathrm{TIR}} \]

onde:

  • \(n_i\): número de pagamentos restantes do título \(i\).
  • \(\mathrm{FC}_{i,k}\): valor do pagamento \(k\) do título \(i\), em base 100, incluindo cupom e amortização quando aplicável.
  • \(\mathrm{DU}_{i,k}\): dias úteis entre liquidação e pagamento \(k\) do título \(i\).
  • \(t_{i,k} = \mathrm{DU}_{i,k}/252\): prazo até esse pagamento, em anos de 252 dias úteis.
  • \(y_i\): TIR real observada do título \(i\).
  • \(r(t_{i,k}; f_i)\): taxa zero real para o prazo do pagamento, obtida pela curva que mantém os trechos anteriores fixos e usa \(f_i\) no trecho em calibração.

Os forwards e taxas zero já calibrados nos títulos curtos permanecem fixos durante a calibração dos títulos longos. Por isso, cada etapa tem apenas uma incógnita e é resolvida por bisseção quando há mudança de sinal no intervalo pesquisado. A coluna taxa_forward retorna essa incógnita calibrada: em cada linha, ela é constante no trecho que termina no respectivo vencimento. O primeiro trecho começa na liquidação.

Convergência condicional

A bisseção é determinística quando encontra um intervalo que contém uma raiz. A função tenta expandir o limite superior para encontrar esse intervalo, mas pode não encontrá-lo para entradas incompatíveis ou extremos. Nesse caso, a calibração não produz uma curva e lança RuntimeError. A mesma exceção é lançada se a avaliação numérica produzir valor não finito ou a bisseção não convergir em 100 iterações.

Precisão do método

A calibração usa \(\mathrm{DU} / 252\) sem truncamento e soma os valores presentes sem arredondamento. Isso difere de :func:cotacao, que aplica as regras da STN de arredondamento dos fluxos e truncamento da cotação.

Parameters:

Name Type Description Default
data_liquidacao DateLike

Data de liquidação.

required
vencimentos DatesLike

Datas de vencimento das NTN-B.

required
taxas ArrayLike

TIRs reais ao ano, em decimal, na base de 252 dias úteis (ex.: 0.10 para 10%).

required

Returns:

Type Description
DataFrame

pl.DataFrame: Curva zero calibrada pelo bootstrap de forwards. Retorna vazio quando não restarem vencimentos posteriores à liquidação.

Raises:

Type Description
RuntimeError

Se não for possível encontrar um intervalo válido para alguma taxa forward, ocorrer valor não finito na avaliação ou a bisseção não convergir.

Output Columns
  • data_vencimento (Date): Data do vértice da curva.
  • dias_uteis (Int64): Dias úteis entre liquidação e vértice.
  • taxa_tir (Float64): TIR recebida na entrada, em formato decimal.
  • taxa_forward (Float64): Parâmetro calibrado por bisseção para o trecho que termina no vencimento, em formato decimal.
  • taxa_zero (Float64): Taxa zero real anualizada, em formato decimal.

Examples:

>>> from pyield import ntnb
>>> import polars.selectors as cs
>>> # Taxas indicativas da ANBIMA na data de referência.
>>> df = ntnb.dados("16-08-2024")
>>> curva = ntnb.taxas_zero(
...     data_liquidacao="16-08-2024",
...     vencimentos=df["data_vencimento"],
...     taxas=df["taxa_indicativa"],
... )
>>> # Taxas em percentual.
>>> curva.with_columns(cs.starts_with("taxa_") * 100)
shape: (14, 5)
┌─────────────────┬────────────┬──────────┬──────────────┬───────────┐
│ data_vencimento ┆ dias_uteis ┆ taxa_tir ┆ taxa_forward ┆ taxa_zero │
│ ---             ┆ ---        ┆ ---      ┆ ---          ┆ ---       │
│ date            ┆ i64        ┆ f64      ┆ f64          ┆ f64       │
╞═════════════════╪════════════╪══════════╪══════════════╪═══════════╡
│ 2025-05-15      ┆ 185        ┆ 6.3893   ┆ 6.3893       ┆ 6.3893    │
│ 2026-08-15      ┆ 502        ┆ 6.6095   ┆ 6.745466     ┆ 6.614071  │
│ 2027-05-15      ┆ 687        ┆ 6.4164   ┆ 5.853671     ┆ 6.40877   │
│ 2028-08-15      ┆ 1002       ┆ 6.3199   ┆ 6.080954     ┆ 6.305605  │
│ 2029-05-15      ┆ 1186       ┆ 6.1753   ┆ 5.279866     ┆ 6.145816  │
│ …               ┆ …          ┆ …        ┆ …            ┆ …         │
│ 2040-08-15      ┆ 4009       ┆ 5.8873   ┆ 5.6644       ┆ 5.832614  │
│ 2045-05-15      ┆ 5196       ┆ 6.0013   ┆ 6.729968     ┆ 6.036943  │
│ 2050-08-15      ┆ 6511       ┆ 6.0247   ┆ 6.234593     ┆ 6.076832  │
│ 2055-05-15      ┆ 7700       ┆ 5.9926   ┆ 5.521283     ┆ 5.990856  │
│ 2060-08-15      ┆ 9017       ┆ 6.0179   ┆ 6.498313     ┆ 6.064823  │
└─────────────────┴────────────┴──────────┴──────────────┴───────────┘

O bootstrap considera apenas os fluxos posteriores à liquidação:

>>> df = ntnb.dados("15-05-2026")
>>> curva = ntnb.taxas_zero(
...     data_liquidacao="18-05-2026",
...     vencimentos=df["data_vencimento"],
...     taxas=df["taxa_indicativa"],
... )
>>> curva_percentual = curva.with_columns(cs.starts_with("taxa_") * 100)
>>> curva_percentual
shape: (15, 5)
┌─────────────────┬────────────┬──────────┬──────────────┬───────────┐
│ data_vencimento ┆ dias_uteis ┆ taxa_tir ┆ taxa_forward ┆ taxa_zero │
│ ---             ┆ ---        ┆ ---      ┆ ---          ┆ ---       │
│ date            ┆ i64        ┆ f64      ┆ f64          ┆ f64       │
╞═════════════════╪════════════╪══════════╪══════════════╪═══════════╡
│ 2026-08-15      ┆ 64         ┆ 10.2013  ┆ 10.2013      ┆ 10.2013   │
│ 2027-05-15      ┆ 249        ┆ 8.12     ┆ 7.395284     ┆ 8.109613  │
│ 2028-08-15      ┆ 564        ┆ 8.1131   ┆ 8.097792     ┆ 8.103011  │
│ 2029-05-15      ┆ 748        ┆ 8.0391   ┆ 7.808764     ┆ 8.030555  │
│ 2030-08-15      ┆ 1062       ┆ 8.066    ┆ 8.126503     ┆ 8.058915  │
│ …               ┆ …          ┆ …        ┆ …            ┆ …         │
│ 2040-08-15      ┆ 3571       ┆ 7.4812   ┆ 6.670623     ┆ 7.341193  │
│ 2045-05-15      ┆ 4758       ┆ 7.3767   ┆ 6.785801     ┆ 7.202367  │
│ 2050-08-15      ┆ 6073       ┆ 7.3      ┆ 6.642902     ┆ 7.080976  │
│ 2055-05-15      ┆ 7262       ┆ 7.2662   ┆ 6.768381     ┆ 7.029733  │
│ 2060-08-15      ┆ 8579       ┆ 7.2674   ┆ 7.190818     ┆ 7.054446  │
└─────────────────┴────────────┴──────────┴──────────────┴───────────┘

A curva retornada contém apenas os vencimentos informados. Para estimar uma taxa zero em um prazo intermediário, use o método de interpolação desejado. Neste exemplo, flat_forward preserva a hipótese de forwards constantes por trecho usada no bootstrap:

>>> curva_decimal = ntnb.taxas_zero(
...     data_liquidacao="18-05-2026",
...     vencimentos=df["data_vencimento"],
...     taxas=df["taxa_indicativa"],
... )
>>> interpolar = yd.Interpolador(
...     curva_decimal["dias_uteis"],
...     curva_decimal["taxa_zero"],
...     metodo="flat_forward",
... )
>>> du_intermediario = (
...     curva_decimal["dias_uteis"][0] + curva_decimal["dias_uteis"][1]
... ) // 2
>>> 0 < interpolar(du_intermediario) < 1
True
Source code in pyield/tpf/titulos/_ntnb_bootstrap.py
def taxas_zero(
    data_liquidacao: DateLike,
    vencimentos: DatesLike,
    taxas: ArrayLike,
) -> pl.DataFrame:
    r"""
    Calcula a curva zero de NTN-B pelo bootstrap de forwards.

    Retorna apenas os vencimentos informados, com taxas em base 252 dias úteis.
    As entradas podem ser as taxas indicativas da ANBIMA obtidas por
    ``yd.ntnb.dados`` ou TIRs fornecidas pelo usuário.

    O método calcula as taxas zero a partir das TIRs observadas das NTN-B,
    calibrando uma taxa forward por trecho para que os fluxos descontados pela
    curva zero reproduzam a cotação obtida pela TIR de cada título.
    A calibração é iterativa e tem convergência condicional: depende da existência
    de um intervalo válido para cada raiz.

    Notes:
        **Racional**

        Uma TIR desconta todos os fluxos de um título por uma única taxa. Uma
        curva zero, por outro lado, precisa de uma taxa para cada data de fluxo.
        O bootstrap transforma as TIRs em uma estrutura de taxas forward e, a
        partir dela, em taxas zero.

        **Curva de forwards e taxa zero**

        A curva usa como vértices as datas de pagamento dos títulos informados.
        Para cada NTN-B, a taxa forward calibrada é mantida constante desde o
        vencimento anterior até o seu vencimento. A primeira taxa forward começa
        na TIR do título mais curto.

        A taxa zero é obtida acumulando os fatores dos trechos calibrados:

        \[
        r_0 = f_0
        \]

        \[
        (1 + r_i)^{t_i} =
        (1 + r_{i-1})^{t_{i-1}}
        (1 + f_i)^{t_i - t_{i-1}}
        \]

        onde:

        - \(i\): índice dos títulos em ordem de vencimento, começando em zero.
        - \(\mathrm{DU}_i\): dias úteis entre liquidação e vencimento do
          título \(i\).
        - \(t_i = \mathrm{DU}_i/252\): prazo até o vencimento do título
          \(i\), em anos de 252 dias úteis.
        - \(f_i\): taxa forward real do trecho que termina no vencimento
          do título \(i\). O primeiro trecho começa na liquidação.
        - \(r_i\): taxa zero real para o prazo \(t_i\).

        As taxas são expressas ao ano, em decimal, na base de 252 dias úteis.

        **Calibração sequencial**

        Para cada título, do menor para o maior vencimento, a função calcula
        a cotação-alvo pela TIR observada:

        \[
        \mathrm{COT}_i^{\mathrm{TIR}} = \sum_{k=1}^{n_i}
        \frac{\mathrm{FC}_{i,k}}{(1+y_i)^{t_{i,k}}}
        \]

        Em seguida, busca por bisseção o forward \(f_i\) que zera o erro
        de cotação \(E_i(f_i)\):

        \[
        E_i(f_i) =
        \sum_{k=1}^{n_i}
        \frac{\mathrm{FC}_{i,k}}{(1 + r(t_{i,k}; f_i))^{t_{i,k}}}
        - \mathrm{COT}_i^{\mathrm{TIR}}
        \]

        onde:

        - \(n_i\): número de pagamentos restantes do título \(i\).
        - \(\mathrm{FC}_{i,k}\): valor do pagamento \(k\) do título
          \(i\), em base 100, incluindo cupom e amortização quando aplicável.
        - \(\mathrm{DU}_{i,k}\): dias úteis entre liquidação e pagamento
          \(k\) do título \(i\).
        - \(t_{i,k} = \mathrm{DU}_{i,k}/252\): prazo até esse pagamento,
          em anos de 252 dias úteis.
        - \(y_i\): TIR real observada do título \(i\).
        - \(r(t_{i,k}; f_i)\): taxa zero real para o prazo do pagamento,
          obtida pela curva que mantém os trechos anteriores fixos e usa
          \(f_i\) no trecho em calibração.

        Os forwards e taxas zero já calibrados nos títulos curtos permanecem
        fixos durante a calibração dos títulos longos. Por isso, cada etapa tem
        apenas uma incógnita e é resolvida por bisseção quando há mudança de
        sinal no intervalo pesquisado. A coluna ``taxa_forward`` retorna essa
        incógnita calibrada: em cada linha, ela é constante no trecho que termina
        no respectivo vencimento. O primeiro trecho começa na liquidação.

        **Convergência condicional**

        A bisseção é determinística quando encontra um intervalo que contém uma
        raiz. A função tenta expandir o limite superior para encontrar esse
        intervalo, mas pode não encontrá-lo para entradas incompatíveis ou
        extremos. Nesse caso, a calibração não produz uma curva e lança
        ``RuntimeError``. A mesma exceção é lançada se a avaliação numérica
        produzir valor não finito ou a bisseção não convergir em 100 iterações.

        **Precisão do método**

        A calibração usa \(\mathrm{DU} / 252\) sem truncamento e soma os valores
        presentes sem arredondamento. Isso difere de :func:`cotacao`, que
        aplica as regras da STN de arredondamento dos fluxos e truncamento da
        cotação.

    Args:
        data_liquidacao: Data de liquidação.
        vencimentos: Datas de vencimento das NTN-B.
        taxas: TIRs reais ao ano, em decimal, na base de 252 dias
            úteis (ex.: 0.10 para 10%).

    Returns:
        pl.DataFrame: Curva zero calibrada pelo bootstrap de forwards. Retorna vazio quando
            não restarem vencimentos posteriores à liquidação.

    Raises:
        RuntimeError: Se não for possível encontrar um intervalo válido para
            alguma taxa forward, ocorrer valor não finito na avaliação ou
            a bisseção não convergir.

    Output Columns:
        - data_vencimento (Date): Data do vértice da curva.
        - dias_uteis (Int64): Dias úteis entre liquidação e vértice.
        - taxa_tir (Float64): TIR recebida na entrada, em formato decimal.
        - taxa_forward (Float64): Parâmetro calibrado por bisseção para o trecho
            que termina no vencimento, em formato decimal.
        - taxa_zero (Float64): Taxa zero real anualizada, em formato decimal.

    Examples:
        >>> from pyield import ntnb
        >>> import polars.selectors as cs
        >>> # Taxas indicativas da ANBIMA na data de referência.
        >>> df = ntnb.dados("16-08-2024")
        >>> curva = ntnb.taxas_zero(
        ...     data_liquidacao="16-08-2024",
        ...     vencimentos=df["data_vencimento"],
        ...     taxas=df["taxa_indicativa"],
        ... )
        >>> # Taxas em percentual.
        >>> curva.with_columns(cs.starts_with("taxa_") * 100)
        shape: (14, 5)
        ┌─────────────────┬────────────┬──────────┬──────────────┬───────────┐
        │ data_vencimento ┆ dias_uteis ┆ taxa_tir ┆ taxa_forward ┆ taxa_zero │
        │ ---             ┆ ---        ┆ ---      ┆ ---          ┆ ---       │
        │ date            ┆ i64        ┆ f64      ┆ f64          ┆ f64       │
        ╞═════════════════╪════════════╪══════════╪══════════════╪═══════════╡
        │ 2025-05-15      ┆ 185        ┆ 6.3893   ┆ 6.3893       ┆ 6.3893    │
        │ 2026-08-15      ┆ 502        ┆ 6.6095   ┆ 6.745466     ┆ 6.614071  │
        │ 2027-05-15      ┆ 687        ┆ 6.4164   ┆ 5.853671     ┆ 6.40877   │
        │ 2028-08-15      ┆ 1002       ┆ 6.3199   ┆ 6.080954     ┆ 6.305605  │
        │ 2029-05-15      ┆ 1186       ┆ 6.1753   ┆ 5.279866     ┆ 6.145816  │
        │ …               ┆ …          ┆ …        ┆ …            ┆ …         │
        │ 2040-08-15      ┆ 4009       ┆ 5.8873   ┆ 5.6644       ┆ 5.832614  │
        │ 2045-05-15      ┆ 5196       ┆ 6.0013   ┆ 6.729968     ┆ 6.036943  │
        │ 2050-08-15      ┆ 6511       ┆ 6.0247   ┆ 6.234593     ┆ 6.076832  │
        │ 2055-05-15      ┆ 7700       ┆ 5.9926   ┆ 5.521283     ┆ 5.990856  │
        │ 2060-08-15      ┆ 9017       ┆ 6.0179   ┆ 6.498313     ┆ 6.064823  │
        └─────────────────┴────────────┴──────────┴──────────────┴───────────┘

        O bootstrap considera apenas os fluxos posteriores à liquidação:
        >>> df = ntnb.dados("15-05-2026")
        >>> curva = ntnb.taxas_zero(
        ...     data_liquidacao="18-05-2026",
        ...     vencimentos=df["data_vencimento"],
        ...     taxas=df["taxa_indicativa"],
        ... )
        >>> curva_percentual = curva.with_columns(cs.starts_with("taxa_") * 100)
        >>> curva_percentual
        shape: (15, 5)
        ┌─────────────────┬────────────┬──────────┬──────────────┬───────────┐
        │ data_vencimento ┆ dias_uteis ┆ taxa_tir ┆ taxa_forward ┆ taxa_zero │
        │ ---             ┆ ---        ┆ ---      ┆ ---          ┆ ---       │
        │ date            ┆ i64        ┆ f64      ┆ f64          ┆ f64       │
        ╞═════════════════╪════════════╪══════════╪══════════════╪═══════════╡
        │ 2026-08-15      ┆ 64         ┆ 10.2013  ┆ 10.2013      ┆ 10.2013   │
        │ 2027-05-15      ┆ 249        ┆ 8.12     ┆ 7.395284     ┆ 8.109613  │
        │ 2028-08-15      ┆ 564        ┆ 8.1131   ┆ 8.097792     ┆ 8.103011  │
        │ 2029-05-15      ┆ 748        ┆ 8.0391   ┆ 7.808764     ┆ 8.030555  │
        │ 2030-08-15      ┆ 1062       ┆ 8.066    ┆ 8.126503     ┆ 8.058915  │
        │ …               ┆ …          ┆ …        ┆ …            ┆ …         │
        │ 2040-08-15      ┆ 3571       ┆ 7.4812   ┆ 6.670623     ┆ 7.341193  │
        │ 2045-05-15      ┆ 4758       ┆ 7.3767   ┆ 6.785801     ┆ 7.202367  │
        │ 2050-08-15      ┆ 6073       ┆ 7.3      ┆ 6.642902     ┆ 7.080976  │
        │ 2055-05-15      ┆ 7262       ┆ 7.2662   ┆ 6.768381     ┆ 7.029733  │
        │ 2060-08-15      ┆ 8579       ┆ 7.2674   ┆ 7.190818     ┆ 7.054446  │
        └─────────────────┴────────────┴──────────┴──────────────┴───────────┘

        A curva retornada contém apenas os vencimentos informados. Para estimar
        uma taxa zero em um prazo intermediário, use o método de interpolação
        desejado. Neste exemplo, ``flat_forward`` preserva a hipótese de
        forwards constantes por trecho usada no bootstrap:
        >>> curva_decimal = ntnb.taxas_zero(
        ...     data_liquidacao="18-05-2026",
        ...     vencimentos=df["data_vencimento"],
        ...     taxas=df["taxa_indicativa"],
        ... )
        >>> interpolar = yd.Interpolador(
        ...     curva_decimal["dias_uteis"],
        ...     curva_decimal["taxa_zero"],
        ...     metodo="flat_forward",
        ... )
        >>> du_intermediario = (
        ...     curva_decimal["dias_uteis"][0] + curva_decimal["dias_uteis"][1]
        ... ) // 2
        >>> 0 < interpolar(du_intermediario) < 1
        True
    """
    from pyield.tpf.titulos.ntnb import (  # noqa: PLC0415
        _validar_entradas_taxas_zero,
        fluxos_caixa,
    )

    if any_is_empty(data_liquidacao, vencimentos, taxas):
        return pl.DataFrame()

    liquidacao, vencimentos, taxas = _validar_entradas_taxas_zero(
        data_liquidacao, vencimentos, taxas
    )
    if vencimentos.is_empty():
        return pl.DataFrame(
            schema={
                "data_vencimento": pl.Date,
                "dias_uteis": pl.Int64,
                "taxa_tir": pl.Float64,
                "taxa_forward": pl.Float64,
                "taxa_zero": pl.Float64,
            }
        )

    titulos = pl.DataFrame({"data_vencimento": vencimentos, "taxa_tir": taxas}).sort(
        "data_vencimento"
    )
    vencimentos_ordenados = titulos["data_vencimento"].to_list()
    fluxos_titulos = [
        fluxos_caixa(liquidacao, vencimento) for vencimento in vencimentos_ordenados
    ]
    return _bootstrap(liquidacao, titulos, fluxos_titulos)

vencimentos(data)

Busca os vencimentos de NTN-B disponíveis para a data de referência.

Parameters:

Name Type Description Default
data DateLike

Data da consulta.

required

Returns:

Type Description
Series

pl.Series: Série de datas de vencimento de NTN-B.

Examples:

>>> from pyield import ntnb
>>> ntnb.vencimentos("16-08-2024")
shape: (14,)
Series: 'data_vencimento' [date]
[
    2025-05-15
    2026-08-15
    2027-05-15
    2028-08-15
    2029-05-15
    …
    2040-08-15
    2045-05-15
    2050-08-15
    2055-05-15
    2060-08-15
]
Source code in pyield/tpf/titulos/ntnb.py
def vencimentos(data: DateLike) -> pl.Series:
    """
    Busca os vencimentos de NTN-B disponíveis para a data de referência.

    Args:
        data (DateLike): Data da consulta.

    Returns:
        pl.Series: Série de datas de vencimento de NTN-B.

    Examples:
        >>> from pyield import ntnb
        >>> ntnb.vencimentos("16-08-2024")
        shape: (14,)
        Series: 'data_vencimento' [date]
        [
            2025-05-15
            2026-08-15
            2027-05-15
            2028-08-15
            2029-05-15
            …
            2040-08-15
            2045-05-15
            2050-08-15
            2055-05-15
            2060-08-15
        ]
    """
    return dados(data)["data_vencimento"]