Pular para conteúdo

NTN-B

bei_rates(settlement, ntnb_maturities, ntnb_rates, nominal_maturities, nominal_rates)

Calcula a inflação implícita para NTN-B a partir de taxas nominais e reais.

A inflação implícita (breakeven) é a que iguala yields reais e nominais, baseada nas taxas zero das NTN-B.

Parameters:

Name Type Description Default
settlement DateLike

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

required
ntnb_maturities ArrayLike

Vencimentos das NTN-B.

required
ntnb_rates ArrayLike

TIRs reais correspondentes.

required
nominal_maturities ArrayLike

Vencimentos de referência para taxas nominais.

required
nominal_rates ArrayLike

Taxas nominais (ex.: DI Futuro).

required

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_zero (Float64): Taxa real zero via bootstrap.
  • taxa_nominal (Float64): Taxa nominal interpolada.
  • inflacao_implicita (Float64): Inflação implícita (breakeven).
Notes

A inflação implícita indica a expectativa de mercado entre liquidação e vencimento.

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.data("05-09-2024")

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

>>> df_di = yd.di1.data("05-09-2024")

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

>>> yd.ntnb.bei_rates(
...     settlement="05-09-2024",
...     ntnb_maturities=df_ntnb["data_vencimento"],
...     ntnb_rates=df_ntnb["taxa_indicativa"],
...     nominal_maturities=df_di["data_vencimento"],
...     nominal_rates=df_di["taxa_ajuste"],
... )
shape: (14, 5)
┌─────────────────┬────────────┬───────────┬──────────────┬────────────────────┐
│ data_vencimento ┆ dias_uteis ┆ taxa_zero ┆ taxa_nominal ┆ inflacao_implicita │
│ ---             ┆ ---        ┆ ---       ┆ ---          ┆ ---                │
│ date            ┆ i64        ┆ f64       ┆ f64          ┆ f64                │
╞═════════════════╪════════════╪═══════════╪══════════════╪════════════════════╡
│ 2025-05-15      ┆ 171        ┆ 0.061748  ┆ 0.113836     ┆ 0.049059           │
│ 2026-08-15      ┆ 488        ┆ 0.066133  ┆ 0.117126     ┆ 0.04783            │
│ 2027-05-15      ┆ 673        ┆ 0.063816  ┆ 0.117169     ┆ 0.050152           │
│ 2028-08-15      ┆ 988        ┆ 0.063635  ┆ 0.11828      ┆ 0.051376           │
│ 2029-05-15      ┆ 1172       ┆ 0.062532  ┆ 0.11838      ┆ 0.052561           │
│ …               ┆ …          ┆ …         ┆ …            ┆ …                  │
│ 2040-08-15      ┆ 3995       ┆ 0.060468  ┆ 0.11759      ┆ 0.053865           │
│ 2045-05-15      ┆ 5182       ┆ 0.0625    ┆ 0.11759      ┆ 0.05185            │
│ 2050-08-15      ┆ 6497       ┆ 0.063016  ┆ 0.11759      ┆ 0.051339           │
│ 2055-05-15      ┆ 7686       ┆ 0.062252  ┆ 0.11759      ┆ 0.052095           │
│ 2060-08-15      ┆ 9003       ┆ 0.063001  ┆ 0.11759      ┆ 0.051354           │
└─────────────────┴────────────┴───────────┴──────────────┴────────────────────┘
Source code in pyield/tn/ntnb.py
def bei_rates(
    settlement: DateLike,
    ntnb_maturities: ArrayLike,
    ntnb_rates: ArrayLike,
    nominal_maturities: ArrayLike,
    nominal_rates: ArrayLike,
) -> pl.DataFrame:
    """
    Calcula a inflação implícita para NTN-B a partir de taxas nominais e reais.

    A inflação implícita (breakeven) é a que iguala yields reais e nominais,
    baseada nas taxas zero das NTN-B.

    Args:
        settlement (DateLike): Data de liquidação da operação.
        ntnb_maturities (ArrayLike): Vencimentos das NTN-B.
        ntnb_rates (ArrayLike): TIRs reais correspondentes.
        nominal_maturities (ArrayLike): Vencimentos de referência para taxas nominais.
        nominal_rates (ArrayLike): Taxas nominais (ex.: DI Futuro).

    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_zero (Float64): Taxa real zero via bootstrap.
        - taxa_nominal (Float64): Taxa nominal interpolada.
        - inflacao_implicita (Float64): Inflação implícita (breakeven).

    Notes:
        A inflação implícita indica a expectativa de mercado entre
        liquidação e vencimento.

    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.data("05-09-2024")

        Busca as taxas de ajuste do DI Futuro para a mesma data de referência:
        >>> df_di = yd.di1.data("05-09-2024")

        Calcula a inflação implícita na data de referência:
        >>> yd.ntnb.bei_rates(
        ...     settlement="05-09-2024",
        ...     ntnb_maturities=df_ntnb["data_vencimento"],
        ...     ntnb_rates=df_ntnb["taxa_indicativa"],
        ...     nominal_maturities=df_di["data_vencimento"],
        ...     nominal_rates=df_di["taxa_ajuste"],
        ... )
        shape: (14, 5)
        ┌─────────────────┬────────────┬───────────┬──────────────┬────────────────────┐
        │ data_vencimento ┆ dias_uteis ┆ taxa_zero ┆ taxa_nominal ┆ inflacao_implicita │
        │ ---             ┆ ---        ┆ ---       ┆ ---          ┆ ---                │
        │ date            ┆ i64        ┆ f64       ┆ f64          ┆ f64                │
        ╞═════════════════╪════════════╪═══════════╪══════════════╪════════════════════╡
        │ 2025-05-15      ┆ 171        ┆ 0.061748  ┆ 0.113836     ┆ 0.049059           │
        │ 2026-08-15      ┆ 488        ┆ 0.066133  ┆ 0.117126     ┆ 0.04783            │
        │ 2027-05-15      ┆ 673        ┆ 0.063816  ┆ 0.117169     ┆ 0.050152           │
        │ 2028-08-15      ┆ 988        ┆ 0.063635  ┆ 0.11828      ┆ 0.051376           │
        │ 2029-05-15      ┆ 1172       ┆ 0.062532  ┆ 0.11838      ┆ 0.052561           │
        │ …               ┆ …          ┆ …         ┆ …            ┆ …                  │
        │ 2040-08-15      ┆ 3995       ┆ 0.060468  ┆ 0.11759      ┆ 0.053865           │
        │ 2045-05-15      ┆ 5182       ┆ 0.0625    ┆ 0.11759      ┆ 0.05185            │
        │ 2050-08-15      ┆ 6497       ┆ 0.063016  ┆ 0.11759      ┆ 0.051339           │
        │ 2055-05-15      ┆ 7686       ┆ 0.062252  ┆ 0.11759      ┆ 0.052095           │
        │ 2060-08-15      ┆ 9003       ┆ 0.063001  ┆ 0.11759      ┆ 0.051354           │
        └─────────────────┴────────────┴───────────┴──────────────┴────────────────────┘
    """
    if any_is_empty(
        settlement, ntnb_maturities, ntnb_rates, nominal_maturities, nominal_rates
    ):
        return pl.DataFrame()
    # Normaliza datas de entrada
    liquidacao = conversores.converter_datas(settlement)
    ntnb_maturities = conversores.converter_datas(ntnb_maturities)

    interpolador_ff = interpolador.Interpolator(
        method="flat_forward",
        known_bdays=bday.count(liquidacao, nominal_maturities),
        known_rates=nominal_rates,
        extrapolate=True,
    )
    df_spot = spot_rates(liquidacao, ntnb_maturities, ntnb_rates)
    df = (
        df_spot.with_columns(
            taxa_nominal=interpolador_ff(df_spot["dias_uteis"]),
        )
        .with_columns(
            inflacao_implicita=(
                (pl.col("taxa_nominal") + 1) / (pl.col("taxa_zero") + 1)
            )
            - 1,
        )
        .select(
            "data_vencimento",
            "dias_uteis",
            "taxa_zero",
            "taxa_nominal",
            "inflacao_implicita",
        )
    )

    return df

cash_flows(settlement, maturity)

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

Parameters:

Name Type Description Default
settlement DateLike

Data de liquidação (exclusiva).

required
maturity DateLike

Data de vencimento.

required

Returns:

Type Description
DataFrame

pl.DataFrame: DataFrame com as colunas de fluxo.

Output Columns
  • data_pagamento (Date): Data de pagamento.
  • valor_pagamento (Float64): Valor do pagamento.

Examples:

>>> from pyield import ntnb
>>> ntnb.cash_flows("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/tn/ntnb.py
def cash_flows(
    settlement: DateLike,
    maturity: DateLike,
) -> pl.DataFrame:
    """
    Gera os fluxos de caixa da NTN-B entre liquidação e vencimento.

    Args:
        settlement (DateLike): Data de liquidação (exclusiva).
        maturity (DateLike): Data de vencimento.

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

    Output Columns:
        - data_pagamento (Date): Data de pagamento.
        - valor_pagamento (Float64): Valor do pagamento.

    Examples:
        >>> from pyield import ntnb
        >>> ntnb.cash_flows("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(settlement, maturity):
        return pl.DataFrame(
            schema={"data_pagamento": pl.Date, "valor_pagamento": pl.Float64}
        )

    # Obtém as datas de cupom entre liquidação e vencimento
    liquidacao = conversores.converter_datas(settlement)
    vencimento = conversores.converter_datas(maturity)
    datas_pagamento = payment_dates(liquidacao, vencimento)

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

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

    return df

data(date)

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

Parameters:

Name Type Description Default
date DateLike

Data de referência para a consulta.

required

Returns:

Type Description
DataFrame

pl.DataFrame: DataFrame Polars com os dados de NTN-B.

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.
  • dv01_usd (Float64): DV01 convertido para USD pela PTAX do dia.
  • 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.data("23-08-2024")
Source code in pyield/tn/ntnb.py
def data(date: DateLike) -> pl.DataFrame:
    """
    Busca as taxas indicativas de NTN-B para a data de referência.

    Args:
        date (DateLike): Data de referência para a consulta.

    Returns:
        pl.DataFrame: DataFrame Polars com os dados de NTN-B.

    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.
        - dv01_usd (Float64): DV01 convertido para USD pela PTAX do dia.
        - 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.data("23-08-2024")  # doctest: +SKIP
    """
    from pyield.b3 import di1  # noqa: PLC0415

    df = utils.obter_tpf(date, "NTN-B")
    if df.is_empty():
        return df

    data_ref = conversores.converter_datas(date)

    # Adiciona dias_uteis (dado derivado, não vem da ANBIMA)
    df = df.with_columns(
        dias_uteis=bday.count_expr("data_referencia", "data_vencimento"),
    )

    # Adiciona duration, prazo_medio, dv01 e dv01_usd
    df = utils.adicionar_duration(df, duration)
    df = utils.adicionar_dv01(df, data_ref)

    # Busca curva DI bruta e calcula taxa_zero, taxa_di e inflação implícita
    df_di = di1.data(date)
    df_bei = bei_rates(
        settlement=date,
        ntnb_maturities=df["data_vencimento"],
        ntnb_rates=df["taxa_indicativa"],
        nominal_maturities=df_di["data_vencimento"],
        nominal_rates=df_di["taxa_ajuste"],
    ).select(
        pl.col("data_vencimento"),
        pl.col("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
    taxas_forward = fwd.forwards(bdays=df["dias_uteis"], rates=df["taxa_zero"])
    df = df.with_columns(taxa_forward=taxas_forward)

    return df.select(
        "data_referencia",
        "titulo",
        "codigo_selic",
        "data_base",
        "data_vencimento",
        "dias_uteis",
        "duration",
        "prazo_medio",
        "dv01",
        "dv01_usd",
        "pu",
        "taxa_compra",
        "taxa_venda",
        "taxa_indicativa",
        "taxa_di",
        "taxa_zero",
        "taxa_forward",
        "inflacao_implicita",
    )

duration(settlement, maturity, rate)

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

Fórmula

Sum( t * CFₜ / (1 + y)ᵗ )

 MacD = ---------------------------------
                 Current Bond Price
Onde

t = tempo (anos) até o pagamento CFₜ = fluxo no tempo t y = TIR (periódica) Price = Soma( CFₜ / (1 + y)ᵗ )

Parameters:

Name Type Description Default
settlement DateLike

Data de liquidação.

required
maturity DateLike

Data de vencimento.

required
rate float

Taxa de desconto usada no cálculo.

required

Returns:

Name Type Description
float float

Macaulay duration em anos úteis.

Examples: >>> from pyield import ntnb >>> ntnb.duration("23-08-2024", "15-08-2060", 0.061005) 15.08305431313046

Source code in pyield/tn/ntnb.py
def duration(
    settlement: DateLike,
    maturity: DateLike,
    rate: float,
) -> float:
    """
    Calcula a Macaulay duration da NTN-B em anos úteis.

    Fórmula:
                   Sum( t * CFₜ / (1 + y)ᵗ )
         MacD = ---------------------------------
                         Current Bond Price

    Onde:
        t    = tempo (anos) até o pagamento
        CFₜ = fluxo no tempo t
        y    = TIR (periódica)
        Price = Soma( CFₜ / (1 + y)ᵗ )

    Args:
        settlement (DateLike): Data de liquidação.
        maturity (DateLike): Data de vencimento.
        rate (float): Taxa de desconto usada no cálculo.

    Returns:
        float: Macaulay duration em anos úteis.

     Examples:
         >>> from pyield import ntnb
         >>> ntnb.duration("23-08-2024", "15-08-2060", 0.061005)
         15.08305431313046
    """
    if any_is_empty(settlement, maturity, rate):
        return float("nan")

    df_fluxos = cash_flows(settlement, maturity)
    if df_fluxos.is_empty():
        return float("nan")

    anos_uteis = bday.count(settlement, df_fluxos["data_pagamento"]) / 252
    vp = df_fluxos["valor_pagamento"] / (1 + rate) ** anos_uteis
    duracao = float((vp * anos_uteis).sum()) / float(vp.sum())
    # Truncar para 14 casas decimais para repetibilidade dos resultados
    return utils.truncate(duracao, 14)

dv01(settlement, maturity, rate, vna)

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

Representa a variação de preço para um aumento de 1 bp (0,01%) na taxa.

Parameters:

Name Type Description Default
settlement DateLike

Data de liquidação.

required
maturity DateLike

Data de vencimento.

required
rate float

Taxa de desconto (TIR) da NTN-B.

required

Returns:

Name Type Description
float float

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

Examples:

>>> from pyield import ntnb
>>> ntnb.dv01("26-03-2025", "15-08-2060", 0.074358, 4470.979474)
4.640875999999935
Source code in pyield/tn/ntnb.py
def dv01(
    settlement: DateLike,
    maturity: DateLike,
    rate: float,
    vna: float,
) -> float:
    """
    Calcula o DV01 (Dollar Value of 01) da NTN-B em R$.

    Representa a variação de preço para um aumento de 1 bp (0,01%) na taxa.

    Args:
        settlement (DateLike): Data de liquidação.
        maturity (DateLike): Data de vencimento.
        rate (float): Taxa de desconto (TIR) da NTN-B.

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

    Examples:
        >>> from pyield import ntnb
        >>> ntnb.dv01("26-03-2025", "15-08-2060", 0.074358, 4470.979474)
        4.640875999999935
    """
    if any_is_empty(settlement, maturity, rate, vna):
        return float("nan")

    cotacao_1 = quotation(settlement, maturity, rate)
    cotacao_2 = quotation(settlement, maturity, rate + 0.0001)
    preco_1 = price(vna, cotacao_1)
    preco_2 = price(vna, cotacao_2)
    return preco_1 - preco_2

forwards(date, zero_coupon=True)

Calcula as taxas forward da NTN-B para a data de referência.

Parameters:

Name Type Description Default
date DateLike

Data de referência para a consulta.

required
zero_coupon bool

Se True, usa taxas zero cupom no cálculo. Padrão True. Se False, usa as TIRs.

True

Returns:

Type Description
DataFrame

pl.DataFrame: DataFrame com as taxas forward.

Output Columns
  • data_vencimento (Date): Data de vencimento.
  • dias_uteis (Int64): Dias úteis entre referência e vencimento.
  • taxa_indicativa (Float64): Taxa indicativa (spot ou TIR).
  • taxa_forward (Float64): Taxa forward calculada.

Examples:

>>> from pyield import ntnb
>>> ntnb.forwards("17-10-2025", zero_coupon=True)
shape: (13, 4)
┌─────────────────┬────────────┬─────────────────┬──────────────┐
│ data_vencimento ┆ dias_uteis ┆ taxa_indicativa ┆ taxa_forward │
│ ---             ┆ ---        ┆ ---             ┆ ---          │
│ date            ┆ i64        ┆ f64             ┆ f64          │
╞═════════════════╪════════════╪═════════════════╪══════════════╡
│ 2026-08-15      ┆ 207        ┆ 0.10089         ┆ 0.10089      │
│ 2027-05-15      ┆ 392        ┆ 0.088776        ┆ 0.074793     │
│ 2028-08-15      ┆ 707        ┆ 0.083615        ┆ 0.076598     │
│ 2029-05-15      ┆ 891        ┆ 0.0818          ┆ 0.074148     │
│ 2030-08-15      ┆ 1205       ┆ 0.080902        ┆ 0.077857     │
│ …               ┆ …          ┆ …               ┆ …            │
│ 2040-08-15      ┆ 3714       ┆ 0.076067        ┆ 0.070587     │
│ 2045-05-15      ┆ 4901       ┆ 0.075195        ┆ 0.069811     │
│ 2050-08-15      ┆ 6216       ┆ 0.074087        ┆ 0.064348     │
│ 2055-05-15      ┆ 7405       ┆ 0.073702        ┆ 0.067551     │
│ 2060-08-15      ┆ 8722       ┆ 0.073795        ┆ 0.074505     │
└─────────────────┴────────────┴─────────────────┴──────────────┘
Source code in pyield/tn/ntnb.py
def forwards(
    date: DateLike,
    zero_coupon: bool = True,
) -> pl.DataFrame:
    """
    Calcula as taxas forward da NTN-B para a data de referência.

    Args:
        date (DateLike): Data de referência para a consulta.
        zero_coupon (bool, optional): Se True, usa taxas zero cupom no cálculo.
            Padrão True. Se False, usa as TIRs.

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

    Output Columns:
        - data_vencimento (Date): Data de vencimento.
        - dias_uteis (Int64): Dias úteis entre referência e vencimento.
        - taxa_indicativa (Float64): Taxa indicativa (spot ou TIR).
        - taxa_forward (Float64): Taxa forward calculada.

    Examples:
        >>> from pyield import ntnb
        >>> ntnb.forwards("17-10-2025", zero_coupon=True)
        shape: (13, 4)
        ┌─────────────────┬────────────┬─────────────────┬──────────────┐
        │ data_vencimento ┆ dias_uteis ┆ taxa_indicativa ┆ taxa_forward │
        │ ---             ┆ ---        ┆ ---             ┆ ---          │
        │ date            ┆ i64        ┆ f64             ┆ f64          │
        ╞═════════════════╪════════════╪═════════════════╪══════════════╡
        │ 2026-08-15      ┆ 207        ┆ 0.10089         ┆ 0.10089      │
        │ 2027-05-15      ┆ 392        ┆ 0.088776        ┆ 0.074793     │
        │ 2028-08-15      ┆ 707        ┆ 0.083615        ┆ 0.076598     │
        │ 2029-05-15      ┆ 891        ┆ 0.0818          ┆ 0.074148     │
        │ 2030-08-15      ┆ 1205       ┆ 0.080902        ┆ 0.077857     │
        │ …               ┆ …          ┆ …               ┆ …            │
        │ 2040-08-15      ┆ 3714       ┆ 0.076067        ┆ 0.070587     │
        │ 2045-05-15      ┆ 4901       ┆ 0.075195        ┆ 0.069811     │
        │ 2050-08-15      ┆ 6216       ┆ 0.074087        ┆ 0.064348     │
        │ 2055-05-15      ┆ 7405       ┆ 0.073702        ┆ 0.067551     │
        │ 2060-08-15      ┆ 8722       ┆ 0.073795        ┆ 0.074505     │
        └─────────────────┴────────────┴─────────────────┴──────────────┘
    """
    if any_is_empty(date):
        return pl.DataFrame()

    # Valida e normaliza a data
    df = data(date).select("data_vencimento", "dias_uteis", "taxa_indicativa")
    if zero_coupon:
        df_ref = spot_rates(
            settlement=date,
            maturities=df["data_vencimento"],
            rates=df["taxa_indicativa"],
        ).rename({"taxa_zero": "taxa_referencia"})
    else:
        df_ref = df.rename({"taxa_indicativa": "taxa_referencia"})
    taxas_forward = fwd.forwards(
        bdays=df_ref["dias_uteis"], rates=df_ref["taxa_referencia"]
    )
    df_ref = df_ref.with_columns(taxa_forward=taxas_forward)
    df = df.join(
        df_ref.select("data_vencimento", "taxa_forward"),
        on="data_vencimento",
        how="inner",
    ).sort("data_vencimento")
    return df

maturities(date)

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

Parameters:

Name Type Description Default
date DateLike

Data de referência para a consulta.

required

Returns:

Type Description
Series

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

Examples:

>>> from pyield import ntnb
>>> ntnb.maturities("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/tn/ntnb.py
def maturities(date: DateLike) -> pl.Series:
    """
    Busca os vencimentos de NTN-B disponíveis para a data de referência.

    Args:
        date (DateLike): Data de referência para a consulta.

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

    Examples:
        >>> from pyield import ntnb
        >>> ntnb.maturities("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 data(date)["data_vencimento"]

payment_dates(settlement, maturity)

Gera todas as datas de cupom entre liquidação e vencimento (inclusivas).

Os cupons são pagos em 15/02, 15/05, 15/08 e 15/11. A NTN-B é definida pela data de vencimento.

Parameters:

Name Type Description Default
settlement DateLike

Data de liquidação (exclusiva).

required
maturity DateLike

Data de vencimento.

required

Returns:

Type Description
Series

pl.Series: Série de datas de cupom no intervalo. Retorna série vazia se vencimento for menor ou igual à liquidação.

Examples:

>>> from pyield import ntnb
>>> ntnb.payment_dates("10-05-2024", "15-05-2025")
shape: (3,)
Series: 'datas_pagamento' [date]
[
    2024-05-15
    2024-11-15
    2025-05-15
]
Source code in pyield/tn/ntnb.py
def payment_dates(
    settlement: DateLike,
    maturity: DateLike,
) -> pl.Series:
    """
    Gera todas as datas de cupom entre liquidação e vencimento (inclusivas).

    Os cupons são pagos em 15/02, 15/05, 15/08 e 15/11. A NTN-B é definida
    pela data de vencimento.

    Args:
        settlement (DateLike): Data de liquidação (exclusiva).
        maturity (DateLike): Data de vencimento.

    Returns:
        pl.Series: Série de datas de cupom no intervalo. Retorna série vazia se
            vencimento for menor ou igual à liquidação.

    Examples:
        >>> from pyield import ntnb
        >>> ntnb.payment_dates("10-05-2024", "15-05-2025")
        shape: (3,)
        Series: 'datas_pagamento' [date]
        [
            2024-05-15
            2024-11-15
            2025-05-15
        ]
    """
    if any_is_empty(settlement, maturity):
        return pl.Series(name="datas_pagamento", dtype=pl.Date)

    liquidacao = conversores.converter_datas(settlement)
    vencimento = conversores.converter_datas(maturity)

    if vencimento <= liquidacao:
        return pl.Series(name="datas_pagamento", dtype=pl.Date)

    data_cupom = vencimento
    datas_cupons = []

    while data_cupom > liquidacao:
        datas_cupons.append(data_cupom)
        data_cupom = utils.subtrair_meses(data_cupom, 6)

    return pl.Series(name="datas_pagamento", values=datas_cupons).sort()

price(vna, quotation)

Calcula o preço (PU) da NTN-B pelas regras da ANBIMA.

Parameters:

Name Type Description Default
vna float

Valor nominal atualizado (VNA).

required
quotation float

Cotação da NTN-B em base 100.

required

Returns:

Name Type Description
float float

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

References
  • https://www.anbima.com.br/data/files/A0/02/CC/70/8FEFC8104606BDC8B82BA2A8/Metodologias%20ANBIMA%20de%20Precificacao%20Titulos%20Publicos.pdf

Examples:

>>> from pyield import ntnb
>>> ntnb.price(4299.160173, 99.3651)
4271.864805
>>> ntnb.price(4315.498383, 100.6409)
4343.156412
Source code in pyield/tn/ntnb.py
def price(
    vna: float,
    quotation: float,
) -> float:
    """
    Calcula o preço (PU) da NTN-B pelas regras da ANBIMA.

    Args:
        vna (float): Valor nominal atualizado (VNA).
        quotation (float): Cotação da NTN-B em base 100.

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

    References:
        - https://www.anbima.com.br/data/files/A0/02/CC/70/8FEFC8104606BDC8B82BA2A8/Metodologias%20ANBIMA%20de%20Precificacao%20Titulos%20Publicos.pdf

    Examples:
        >>> from pyield import ntnb
        >>> ntnb.price(4299.160173, 99.3651)
        4271.864805
        >>> ntnb.price(4315.498383, 100.6409)
        4343.156412
    """
    if any_is_empty(vna, quotation):
        return float("nan")
    return utils.truncate(vna * quotation / 100, 6)

quotation(settlement, maturity, rate)

Calcula a cotação da NTN-B em base 100 pelas regras da ANBIMA.

Parameters:

Name Type Description Default
settlement DateLike

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

required
maturity DateLike

Data de vencimento da NTN-B.

required
rate float

Taxa de desconto (TIR) usada no valor presente.

required

Returns:

Name Type Description
float float

Cotação da NTN-B truncada em 4 casas. Retorna NaN em erro.

References
  • https://www.anbima.com.br/data/files/A0/02/CC/70/8FEFC8104606BDC8B82BA2A8/Metodologias%20ANBIMA%20de%20Precificacao%20Titulos%20Publicos.pdf
  • O cupom semestral é 2,956301, equivalente a 6% a.a. com capitalização semestral e arredondamento para 6 casas, conforme ANBIMA.

Examples:

>>> from pyield import ntnb
>>> ntnb.quotation("31-05-2024", "15-05-2035", 0.061490)
99.3651
>>> ntnb.quotation("31-05-2024", "15-08-2060", 0.061878)
99.5341
>>> ntnb.quotation("15-08-2024", "15-08-2032", 0.05929)
100.6409
Source code in pyield/tn/ntnb.py
def quotation(
    settlement: DateLike,
    maturity: DateLike,
    rate: float,
) -> float:
    """
    Calcula a cotação da NTN-B em base 100 pelas regras da ANBIMA.

    Args:
        settlement (DateLike): Data de liquidação da operação.
        maturity (DateLike): Data de vencimento da NTN-B.
        rate (float): Taxa de desconto (TIR) usada no valor presente.

    Returns:
        float: Cotação da NTN-B truncada em 4 casas. Retorna NaN em erro.

    References:
        - https://www.anbima.com.br/data/files/A0/02/CC/70/8FEFC8104606BDC8B82BA2A8/Metodologias%20ANBIMA%20de%20Precificacao%20Titulos%20Publicos.pdf
        - O cupom semestral é 2,956301, equivalente a 6% a.a. com capitalização
          semestral e arredondamento para 6 casas, conforme ANBIMA.

    Examples:
        >>> from pyield import ntnb
        >>> ntnb.quotation("31-05-2024", "15-05-2035", 0.061490)
        99.3651
        >>> ntnb.quotation("31-05-2024", "15-08-2060", 0.061878)
        99.5341
        >>> ntnb.quotation("15-08-2024", "15-08-2032", 0.05929)
        100.6409
    """
    if any_is_empty(settlement, maturity, rate):
        return float("nan")

    df_fluxos = cash_flows(settlement, maturity)
    if df_fluxos.is_empty():
        return float("nan")

    valores_fluxo = df_fluxos["valor_pagamento"]
    dias_uteis = bday.count(settlement, df_fluxos["data_pagamento"])
    anos_uteis = utils.truncate(dias_uteis / 252, 14)
    fatores_desconto = (1 + rate) ** anos_uteis
    # Calcula o valor presente de cada fluxo com arredondamento ANBIMA
    vp = (valores_fluxo / fatores_desconto).round(10)
    # Retorna a cotação (soma dos valores presentes) com truncamento ANBIMA
    return utils.truncate(vp.sum(), 4)

rate(settlement, maturity, vna, price_value)

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

A função inverte numericamente a cadeia price(vna, quotation(...)), encontrando a taxa que zera a diferença entre o preço calculado e o informado.

Parameters:

Name Type Description Default
settlement DateLike

Data de liquidação.

required
maturity DateLike

Data de vencimento.

required
vna float

Valor nominal atualizado (VNA).

required
price_value float

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

required

Returns:

Name Type Description
float float

TIR implícita em formato decimal. Retorna NaN em caso de erro.

Examples:

>>> from pyield import ntnb
>>> ntnb.rate("31-05-2024", "15-05-2035", 4299.160173, 4271.864805)
0.06149
>>> ntnb.rate("15-08-2024", "15-08-2032", 4315.498383, 4343.156412)
0.05929
Source code in pyield/tn/ntnb.py
def rate(
    settlement: DateLike,
    maturity: DateLike,
    vna: float,
    price_value: float,
) -> float:
    """
    Calcula a TIR implícita de uma NTN-B a partir do preço (PU).

    A função inverte numericamente a cadeia ``price(vna, quotation(...))``,
    encontrando a taxa que zera a diferença entre o preço calculado e o
    informado.

    Args:
        settlement (DateLike): Data de liquidação.
        maturity (DateLike): Data de vencimento.
        vna (float): Valor nominal atualizado (VNA).
        price_value (float): Preço unitário (PU) do título.

    Returns:
        float: TIR implícita em formato decimal. Retorna NaN em
            caso de erro.

    Examples:
        >>> from pyield import ntnb
        >>> ntnb.rate("31-05-2024", "15-05-2035", 4299.160173, 4271.864805)
        0.06149
        >>> ntnb.rate("15-08-2024", "15-08-2032", 4315.498383, 4343.156412)
        0.05929
    """
    if any_is_empty(settlement, maturity, vna, price_value):
        return float("nan")

    if price_value <= 0:
        return float("nan")

    def diferenca_preco(taxa: float) -> float:
        cotacao = quotation(settlement, maturity, taxa)
        return price(vna, cotacao) - price_value

    taxa_encontrada = utils.encontrar_raiz(diferenca_preco)
    return round(taxa_encontrada, 6)

spot_rates(settlement, maturities, rates, show_coupons=False)

Calcula as taxas zero da NTN-B usando bootstrap.

O bootstrap determina as taxas zero a partir dos yields dos títulos, resolvendo iterativamente as taxas que descontam os fluxos ao preço.

Parameters:

Name Type Description Default
settlement DateLike

Data de liquidação.

required
maturities ArrayLike

Datas de vencimento dos títulos.

required
rates ArrayLike

TIRs correspondentes.

required
show_coupons bool

Se True, inclui datas intermediárias de cupom. Padrão False.

False

Returns:

Type Description
DataFrame

pl.DataFrame: DataFrame com as taxas zero.

Output Columns
  • data_vencimento (Date): Data de vencimento.
  • dias_uteis (Int64): Dias úteis entre liquidação e vencimento.
  • taxa_zero (Float64): Taxa zero (real).

Examples:

>>> from pyield import ntnb
>>> # Busca as taxas de NTN-B para uma data de referência
>>> df = ntnb.data("16-08-2024")
>>> # Calcula as taxas zero considerando a liquidação na data de referência
>>> ntnb.spot_rates(
...     settlement="16-08-2024",
...     maturities=df["data_vencimento"],
...     rates=df["taxa_indicativa"],
... )
shape: (14, 3)
┌─────────────────┬────────────┬───────────┐
│ data_vencimento ┆ dias_uteis ┆ taxa_zero │
│ ---             ┆ ---        ┆ ---       │
│ date            ┆ i64        ┆ f64       │
╞═════════════════╪════════════╪═══════════╡
│ 2025-05-15      ┆ 185        ┆ 0.063893  │
│ 2026-08-15      ┆ 502        ┆ 0.066141  │
│ 2027-05-15      ┆ 687        ┆ 0.064087  │
│ 2028-08-15      ┆ 1002       ┆ 0.063057  │
│ 2029-05-15      ┆ 1186       ┆ 0.061458  │
│ …               ┆ …          ┆ …         │
│ 2040-08-15      ┆ 4009       ┆ 0.058326  │
│ 2045-05-15      ┆ 5196       ┆ 0.060371  │
│ 2050-08-15      ┆ 6511       ┆ 0.060772  │
│ 2055-05-15      ┆ 7700       ┆ 0.059909  │
│ 2060-08-15      ┆ 9017       ┆ 0.060652  │
└─────────────────┴────────────┴───────────┘
Notes

O cálculo considera: - Mapear todas as datas de pagamento até o último vencimento. - Interpolar as TIRs nas datas intermediárias. - Calcular a cotação da NTN-B para cada vencimento. - Calcular as taxas zero reais.

Source code in pyield/tn/ntnb.py
def spot_rates(
    settlement: DateLike,
    maturities: ArrayLike,
    rates: ArrayLike,
    show_coupons: bool = False,
) -> pl.DataFrame:
    """
    Calcula as taxas zero da NTN-B usando bootstrap.

    O bootstrap determina as taxas zero a partir dos yields dos títulos,
    resolvendo iterativamente as taxas que descontam os fluxos ao preço.

    Args:
        settlement (DateLike): Data de liquidação.
        maturities (ArrayLike): Datas de vencimento dos títulos.
        rates (ArrayLike): TIRs correspondentes.
        show_coupons (bool, optional): Se True, inclui datas intermediárias de cupom.
            Padrão False.

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

    Output Columns:
        - data_vencimento (Date): Data de vencimento.
        - dias_uteis (Int64): Dias úteis entre liquidação e vencimento.
        - taxa_zero (Float64): Taxa zero (real).

    Examples:
        >>> from pyield import ntnb
        >>> # Busca as taxas de NTN-B para uma data de referência
        >>> df = ntnb.data("16-08-2024")
        >>> # Calcula as taxas zero considerando a liquidação na data de referência
        >>> ntnb.spot_rates(
        ...     settlement="16-08-2024",
        ...     maturities=df["data_vencimento"],
        ...     rates=df["taxa_indicativa"],
        ... )
        shape: (14, 3)
        ┌─────────────────┬────────────┬───────────┐
        │ data_vencimento ┆ dias_uteis ┆ taxa_zero │
        │ ---             ┆ ---        ┆ ---       │
        │ date            ┆ i64        ┆ f64       │
        ╞═════════════════╪════════════╪═══════════╡
        │ 2025-05-15      ┆ 185        ┆ 0.063893  │
        │ 2026-08-15      ┆ 502        ┆ 0.066141  │
        │ 2027-05-15      ┆ 687        ┆ 0.064087  │
        │ 2028-08-15      ┆ 1002       ┆ 0.063057  │
        │ 2029-05-15      ┆ 1186       ┆ 0.061458  │
        │ …               ┆ …          ┆ …         │
        │ 2040-08-15      ┆ 4009       ┆ 0.058326  │
        │ 2045-05-15      ┆ 5196       ┆ 0.060371  │
        │ 2050-08-15      ┆ 6511       ┆ 0.060772  │
        │ 2055-05-15      ┆ 7700       ┆ 0.059909  │
        │ 2060-08-15      ┆ 9017       ┆ 0.060652  │
        └─────────────────┴────────────┴───────────┘

    Notes:
        O cálculo considera:
        - Mapear todas as datas de pagamento até o último vencimento.
        - Interpolar as TIRs nas datas intermediárias.
        - Calcular a cotação da NTN-B para cada vencimento.
        - Calcular as taxas zero reais.
    """
    if any_is_empty(settlement, maturities, rates):
        return pl.DataFrame()

    settlement, maturities, rates = _validar_entradas_taxa_spot(
        settlement, maturities, rates
    )

    df = _criar_df_bootstrap(settlement, rates, maturities)

    # Bootstrap para calcular taxas zero
    linhas = df.to_dicts()
    primeiro_vencimento = maturities.min()
    for linha in linhas:
        vencimento = linha["data_vencimento"]

        # Taxas zero <= primeiro vencimento são TIR por definição
        if vencimento <= primeiro_vencimento:
            taxa_zero = linha["taxa_tir"]
            df = _atualizar_taxa_zero(df, vencimento, taxa_zero)
            continue

        # Calcula taxa zero para o vencimento corrente
        valor_presente_cupons = _calcular_valor_presente_cupons(
            df, settlement, vencimento
        )
        preco_titulo = quotation(settlement, vencimento, linha["taxa_tir"])
        fator_preco = VALOR_FINAL / (preco_titulo - valor_presente_cupons)
        taxa_zero = fator_preco ** (1 / linha["anos_uteis"]) - 1

        df = _atualizar_taxa_zero(df, vencimento, taxa_zero)

    if not show_coupons:
        df = df.filter(pl.col("data_vencimento").is_in(maturities.to_list()))
    return df.select(["data_vencimento", "dias_uteis", "taxa_zero"])