Uma classe de precificação para Bonds (TPF) em Python

milton-rocha
9 min readAug 3, 2022

--

Uma grande dificuldade para o mercado brasileiro surge ao avaliarmos as necessidades, principalmente em programação, relativas à precificação de Títulos Públicos Federais e outros Bonds em Python. Apesar de ser uma linguagem muito difundida, poucas bibliotecas buscam aplicar, de fato, os conceitos do mercado brasileiro de forma otimizada.

Neste post abordarei algumas soluções simples que encontrei para problemas do cotidiano para análises quantitativas tanto em quesitos de análise pura como em construção de cenários para Bonds no mercado de Renda Fixa brasileiro.

O que um precificador básico deveria conter?

Ressalto que, assim como em todos os posts, expresso a minha opinião pessoal sobre o que deveria ser, no mínimo, tratado em cada nível de tema para o seu uso no dia a dia.

Quando analisamos os conceitos mais necessários tanto para traders quanto para analistas/gestores de riscos, devemos tentar consolidar duas formas cruciais de conhecimento, que geralmente são separadas somente por falta de habilidade ou vontade de consolidação em uma forma mais ampla de visão sobre gestão financeira.

  1. Visão trader: A análise dos números enquanto posicionamento direcional
  2. Visão risco: A análise dos números enquanto gestão do risco geral da carteira

Desta forma, o que, na minha opinião, deveria ser abrangido por uma classe de precificação de Bonds no Python?

  1. Visão de preço e risco ampla do ativo
  2. Quebra do risco por fatores parciais
  3. Alocação do risco em buckets predefinidos pelo gestor de risco
  4. Otimização de cálculo nativa da precificação para gestão real-time
  5. Solução numérica para encontro de raizes de taxas (Bond solver)
  6. Report de risco contendo as principais exposições do Bond

A lista poderia continuar infinitamente, mas, creio que com estes 6 itens, se montaria uma excelente classe de precificação de bonds para o mercado brasileiro.

Mãos à obra

Utilizarei-me aqui de alguns artigos que já publiquei neste perfil, dentre estes devo citar:

Para a função de feriados: Código simples em Python para operações de dias úteis (igual do Excel)

Para precificação de Bonds (TPF): Bootstrap da ETTJ Nominal utilizando LTN e NTN-F (aplicação em Python)

Para cálculo de discount factor: DI1 e COPOM — Uma proposta para gestão do risco de taxa de juros no curto prazo

Para as funções de quebra e visualização de riscos: Quebra de risco de taxa de juros de um portfólio

Para a função do Bond Solver: Calculando yield implícito em Bonds via Newton-Raphson

Generalizando as equações

PRECIFICAÇÃO

Partindo dos resumos que já são apresentados nos meus outros artigos, generalizarei aqui as equações de precificação de bonds, de forma a poder programar somente um método que seja capaz de precificar qualquer bond criado.

(1) Fórmula de precificação de ativos

Nesta simples fórmula as variáveis são descritas abaixo:

P(t,T)_Bond : preço do bond

C_y : taxa de cupom anualizada

C_f : frequência anual de cupons (semestral = 2)

YTM : yield to maturity do bond

i-t : prazo, em anos úteis, entre a data de precificação, t, e o vencimento do fluxo de caixa, i

T-t: prazo, em anos úteis, entre a data de precificação, t, e o vencimento do bond, T

FACE : valor de face do bond

VNA : valor nominal atualizado do bond

Mas por qual motivo esta é uma fórmula generalizada?

Caso o título não tenha fluxos de caixa, a fórmula irá considerar somente o último fluxo, dada a sua construção. Caso o título não tenha valor de FACE, é só substituir a variável FACE por 1 que não haverá mudança nos fatores de desconto do título. Caso o título não possua VNA, é só substituir esta variável por 1 que não haverá mudança na multiplicação final.

Ou seja, com valores base atribuídos para algumas variáveis, é possível criar o preço de qualquer bond padronizado que tenha uma série de fluxos de pagamento uniformes para serem descontados à valor presente.

RISCOS

Já é bastante difundido que a Duration de um bond qualquer é dada pela soma produto dos fluxos de caixa descontados à valor presente pelo prazo de vencimento, em percentual de ano, dividido pela soma dos fluxos de caixa descontados à valor presente (ou seja, seu P(t,T)).

Pode-se simplificar ainda mais o conceito e conseguir calculá-lo sem o uso do VNA e valor de FACE, visto que os fluxos de caixa descontados já são o peso financeiro dos fluxos do bond, não é necessário multiplicar pelo valor de FACE e pelo VNA para saber a sua duration.

(2) Fórmula de duration para um bond generalizado

Deriva-se deste conceito, portanto, a duration modificada, que simplesmente será (2) dividido por (1+YTM). E o conceito de DV01 (Dollar Value of a Basis Point), que nada mais é do que a duration modificada multiplicada por 0,01% e, posteriormente, multiplicada pelo valor do bond em questão (P(t,T)).

(3) Duration Modificada e (4) DV01 generalizadas

Agora o último conceito que será apresentado para o cálculo de riscos é o de Convexidade. Este conceito irá se mostrar bastante útil para títulos que possuam vencimentos muito longos, com alta sensibilidade à variações mínimas na taxa de juros, que impactam diretamente na precisão obtida por (3) e (4).

(5) Convexidade generalizada

Essencialmente cada fluxo de caixa será multiplicado por porcentagem de ano ao quadrado mais ela mesma. Depois disso todos os fluxos multiplicados serão divididos pelo preço multiplicado por um ajuste de grau dois no carrego de juros do YTM.

QUEBRA DO RISCO POR FATORES PARCIAIS

Como já comentei acima, a quebra do risco por fatores parciais pode ser encontrada discutida com mais detalhes em Quebra de risco de taxa de juros de um portfólio.

ALOCAÇÃO DE RISCO POR BUCKETS

Este tema (bucketeamento) também foi discutido no artigo citado acima.

SOLUÇÃO NUMÉRICA PARA ENCONTRAR RAÍZES

O método que será utilizado na classe é bem semelhante ao método encontrado em Calculando yield implícito em Bonds via Newton-Raphson.

REPORT DE RISCO DO BOND

Este será apenas a consolidação dos números calculados nas generalizações apresentadas dos fatores de risco, de (2) a (5).

O código em ação

Caso um usuário deseje calcular os preços de fechamento de LTN, NTN-F e NTN-B de um dia, como seria feito?

Consultando a ANBIMA é possível ver que as taxas indicativas registradas para 29/07/2022:

Fonte: ANBIMA
Fonte: ANBIMA
Fonte: ANBIMA

Exemplo: Precificação da NTN-B 2060

Exemplo do código em ação
Gráfico 1. Feito utilizando o método .structured_buckets() do objeto de pricing

Uma precificação simples como essa é amplamente difundida, mas, e se o usuário desejar precificar o bond utilizando a curva de juros característica para a data em questão?

Gráfico 2. ETTJ Pré x DI de 29/07/2022

Precificando NTN-F 2033 com a curva de juros (curve) acoplada no objeto:

Exemplo de cálculo de NTN-F utilizando objeto de curva de juros Flat Forward

Caso o usuário calcule o mesmo objeto, sem a curva de juros embutida o resultado será:

Exemplo de cálculo de NTN-F com curva de juros flat (YTM)

Por padrão o Bond criará uma curva de juros Flat em todos os vencimentos, ou seja, todos os vencimentos possuirão taxa de juros spot igual ao YTM (bond_yield) fornecido para o objeto, fazendo com que o desconto à valor presente seja realizado somente pela Taxa Interna de Retorno (YTM).

Já com o objeto de curva de juros, o bond será precificado através do cálculo, e consequente desconto, de cada um de seus fluxos por uma taxa de juros interpolada utilizando o objeto de curva de juros fornecido.

A construção do objeto padrão utilizado é demonstrada e feita em Objeto de curva de juros com interpolação e extrapolação Flat Forward em Python.

Exemplo: Consolidando uma carteira com diversos papeis

Papeis dentro de uma carteira:

  • LTN : 132.835 unidades da LTN2026 @ 12,7492%
  • NTN-F : 98.475 unidades da NTNF2033 @ 12,9348%
  • NTN-B : 15.684 unidades da NTNB2045 @ 6,2480%

— Qual seria a consolidação desta carteira em termos de KRDV01?

Exemplo de consolidação dos bonds em carteira
Gráfico 3. KRDV01 para o portfólio proposto acima

Qual a vantagem de visualizar desta forma o risco?

Utilizando a classe Bond os riscos podem ser consolidados de forma a quebrar o KRDV01 port tipo de fator de risco que deverá ser hedgeado, ou seja, caso o usuário tenha a carteira acima, existirão riscos em taxa nominal (vindos da LTN e NTN-F) e riscos em taxa real (NTN-B), desta forma, o Gráfico 3 mostra grande utilidade para o hedge das posições.

Utilidade no hedge

Visto que a classe Bond já consolida o KRDV01 em uma tabela prática de ser acessada, o usuário consegue facilmente calcular a quantidade necessária para hedge da sua carteira dados os instrumentos disponíveis no mercado e seu KRDV01 unitário para cada um de seus fluxos.

Análise da performance da classe

Gráfico 4. Performance do código para rodar 1000 bonds diferentes em cada prazo de vencimento

Ao se analisar o profiling do código (via cProfile e pstats), o tempo exato que demorou para rodar uma NTN-F sintética com vencimento em 2122–07–29 (100Y, com 200 fluxos, 200 interpolações individuais por objeto), foi 3,523 segundos, ou ~284 bonds por segundo. O tempo que o código levou para rodar uma NTN-F 2032–07–29 (10Y) foi de 0,490 segundo, ou ~2041 bonds por segundo.

Ao se pensar em termos de simulações possíveis para se gerar, com full revaluation dos bonds de uma carteira, em tempo real, se torna factível simular, por exemplo, 100 cenários de curvas de juros para uma carteira que contém 1.000 títulos com prazo médio de vencimento 10 anos.

O total de cálculos seria 100 (cenários) * 1.000 (títulos diferentes) = 100.000, o que na média de cálculo de 2041 bonds por segundo, levaria ~49 segundos.

Este cenário, por mais que pareça irreal, seria sensato para uma carteira de market maker que deseje calcular o full reval de todos os seus bonds, faz sentido existirem cerca de 1.000 títulos públicos com características diferentes. Mesmo assim, a performance do código é interessante.

Link para o código

Repositório rendafixa

Pontos que sofrerão atualizações no futuro

  • Complementos no que diz respeito ao cálculo de posição necessária para hedge do Bond em questão
  • Criação de mecanismos de Portfólio com consolidação de diversos objetos Bond
  • Simulações do portfólio com choques na curva de juros e impactos na carteira via full revaluation e estimação via ajustes de duration e convexidade

Referências bibliográficas

[Neto et. al. (2019)] José Monteiro Varanda Neto, de Souza Santos, José Carlos, Mello, e Eduardo Morato. O mercado de renda fixa no Brasil: conceitos, precificação e risco. Saint Paul, 2019

[Santos e Silva (2017)] José Santos e Marcos Silva. Derivativos e renda fixa: Teoria e aplicações ao mercado brasileiro, volume 1. Atlas, São Paulo, 2017.

[Preços de ajuste e volume do DI1] Página oficial B3, acesso em: 29/07/2022

[Manual de Curvas B3] Manual de Curvas B3, acesso em: 29/07/2022

METODOLOGIA PARA A APURAÇÃO DE CURVAS DE PREÇOS E DE SPREADS TEÓRICOS DE TÍTULOS PÚBLICOS, acesso em: 29/07/2022

[Preços de ajuste e volume do DI1] Página oficial B3, acesso em: 29/07/2022

--

--