Artigo Técnico

Fórmulas de Obrigações e Tesouro em Delphi com o HotXLS

O HotXLS avalia nativamente as funções financeiras de obrigações e tesouro do Excel em Delphi e C++Builder: PRICE, YIELD, DURATION, MDURATION, as seis primitivas de data de cupão, o trio de bilhetes do tesouro, a família de desconto e as funções de depreciação francesas. Calculam dentro do próprio motor de fórmulas da biblioteca, pelo que um livro que avalia uma carteira de obrigações recalcula num servidor sem Excel e sem o suplemento Analysis ToolPak à vista

Estas funções costumavam viver no Analysis ToolPak, razão pela qual ainda carregam uma reputação de serem incómodas. Desde o Excel 2007 fazem parte do conjunto de funções padrão e são escritas sem prefixo no formato de ficheiro, sem o prefixo _xlfn. que as funções genuinamente mais recentes carregam. Um livro que as utiliza é um livro comum; a única questão é saber se o que quer que o recalcule conhece as funções

O que o motor de datas de cupão tem de acertar

Todos os cálculos de obrigações assentam nas mesmas primitivas: quantos dias desde o último cupão, quantos dias no período de cupão atual, quantos dias até ao próximo cupão, quando é que esses cupões caem, e quantos restam. São elas COUPDAYBS, COUPDAYS, COUPDAYSNC, COUPPCD, COUPNCD e COUPNUM, e todas as funções de preço e de rendimento as chamam

O que as torna não triviais é o argumento da base. Estão em jogo cinco convenções de contagem de dias — 30/360 EUA, real/real, real/360, real/365 e 30/360 europeia — e discordam em questões como o que acontece quando uma data de liquidação cai no dia 31 de um mês, ou como se comporta uma data de cupão de fevereiro num ano bissexto. Acertar numa convenção e aproximar as restantes produz resultados que parecem plausíveis e estão errados por alguns pontos base, o que numa posição grande é dinheiro real

O HotXLS implementa as cinco num motor de datas de cupão aninhado e autónomo, partilhado por todas as funções que dele necessitam, pelo que PRICE e DURATION nunca podem discordar entre si sobre onde estão as datas de cupão

Motor de datas de cupão do HotXLS para Delphi: os dados de entrada da obrigação e a base de contagem de dias selecionada alimentam seis primitivas COUP partilhadas por PRICE, YIELD, DURATION e MDURATION
As cinco bases de contagem de dias alimentam um único motor de datas de cupão aninhado, partilhado por todas as funções financeiras do HotXLS, pelo que PRICE e DURATION nunca podem discordar sobre onde caem as datas de cupão

Preço e rendimento num livro

Não há nada de especial no código que chama: escreva as fórmulas, recalcule, leia os valores. É esse o objetivo — o livro é o modelo, e a sua aplicação Delphi é a calculadora:

uses
  lxHandleX;

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
begin
  Book := TXLSXWorkbook.Create;
  try
    Sheet := Book.Sheets.Add('Bond');
    Sheet.Cells[1, 1].Value := EncodeDate(2026, 3, 15);   // liquidação
    Sheet.Cells[2, 1].Value := EncodeDate(2034, 11, 15);  // maturidade
    Sheet.Cells[3, 1].Value := 0.0425;                    // taxa de cupão
    Sheet.Cells[4, 1].Value := 0.0391;                    // rendimento exigido
    Sheet.Cells[5, 1].Value := 100;                       // reembolso
    Sheet.Cells[6, 1].Value := 2;                         // frequência
    Sheet.Cells[7, 1].Value := 1;                         // base: real/real

    Sheet.Cells[9, 1].Formula  := '=PRICE(A1,A2,A3,A4,A5,A6,A7)';
    Sheet.Cells[10, 1].Formula := '=DURATION(A1,A2,A3,A4,A6,A7)';
    Sheet.Cells[11, 1].Formula := '=MDURATION(A1,A2,A3,A4,A6,A7)';
    Sheet.Cells[12, 1].Formula := '=COUPNUM(A1,A2,A6,A7)';

    Book.Recalculate;

    Writeln(Format('price %.6f  Macaulay %.6f  modified %.6f  coupons %d',
      [Double(Sheet.Cells[9, 1].Value), Double(Sheet.Cells[10, 1].Value),
       Double(Sheet.Cells[11, 1].Value),
       Integer(Sheet.Cells[12, 1].Value)]));
  finally
    Book.Free;
  end;
end;

As datas entram como valores de data reais, e não como strings. Uma data em texto funciona no Excel porque o Excel a converte, e essa conversão depende do idioma/região, que é exatamente o tipo de dependência que um recálculo do lado do servidor não deveria ter. As regras do sistema de datas que estão por baixo estão descritas em números de série de data e o sistema de 1904

O rendimento não tem forma fechada, e isso tem consequências

PRICE é um cálculo direto: descontar os fluxos de caixa, somá-los. YIELD é o seu inverso e não tem solução em forma fechada para uma obrigação com mais de um cupão por vencer, pelo que tem de ser resolvido numericamente. O HotXLS usa um solucionador por bisseção, que converge de forma fiável, em vez de rápida

Seguem-se duas implicações práticas. O rendimento é mensuravelmente mais caro do que o preço, pelo que uma reavaliação de carteira que calcula rendimentos para dez mil posições custa visivelmente mais do que uma que calcula preços, e vale a pena saber qual dos dois o seu modelo realmente precisa. E um rendimento que não pode ser delimitado — porque os dados de entrada descrevem uma obrigação que nenhum rendimento consegue avaliar ao valor dado — devolve um erro, em vez de um número errado, o que é o comportamento correto e aquele que se deve testar

No HotXLS em Delphi, PRICE é uma única passagem direta de fluxos de caixa descontados, enquanto YIELD não tem forma fechada e é resolvido por um ciclo de bisseção que devolve um erro quando não existe um intervalo de delimitação
PRICE é uma única passagem direta sobre os fluxos de caixa descontados, enquanto YIELD não tem forma fechada e é delimitado e bissetado, devolvendo um erro em vez de um número errado quando os dados de entrada não podem ser avaliados

Os bilhetes do tesouro são a sua própria família

Os bilhetes são instrumentos de desconto sem cupões, pelo que usam TBILLPRICE, TBILLYIELD e TBILLEQ, em vez das funções de obrigações. TBILLEQ converte uma taxa de desconto num rendimento equivalente a obrigação, e tem uma peculiaridade documentada que vale a pena conhecer: para maturidades superiores a 182 dias, a conversão é quadrática, e não linear, porque o título abrange mais do que um período de meio ano

Introduzir um bilhete em PRICE, em vez disso, com um cupão zero e uma frequência, não produz o mesmo número. A família de instrumentos de desconto — DISC, INTRATE, RECEIVED, PRICEDISC e YIELDDISC — existe precisamente porque estes instrumentos são cotados e liquidados de forma diferente

O resto do conjunto, num só lugar

Para além de obrigações e bilhetes, o mesmo lote cobre as funções que as rodeiam em modelos reais. PRICEMAT e YIELDMAT tratam de títulos que pagam juros na maturidade. ACCRINTM calcula os juros acumulados para esses. EFFECT e NOMINAL convertem entre taxas anuais efetivas e nominais, que é a conversão mais frequentemente feita mal à mão. CUMIPMT e CUMPRINC dão os juros e o capital acumulados ao longo de um intervalo de períodos. FVSCHEDULE aplica uma série de taxas diferentes em sequência, aceitando um intervalo ou um escalar

DOLLARDE e DOLLARFR convertem entre cotações em dólares decimais e fracionárias, o que importa para qualquer instrumento ainda cotado em trinta e dois avos. E AMORDEGRC e AMORLINC implementam os métodos de depreciação franceses, que são exigidos pelas regras contabilísticas francesas, e não conveniências opcionais

// Uma conversão de taxas que as pessoas costumam fazer mal à mão
Sheet.Cells[1, 3].Formula := '=EFFECT(0.0425,12)';   // nominal para efetiva
Sheet.Cells[2, 3].Formula := '=NOMINAL(0.043338,12)'; // e de volta

// Juros acumulados nos primeiros dois anos de um empréstimo de 25 anos
Sheet.Cells[4, 3].Formula :=
  '=CUMIPMT(0.0389/12,300,340000,1,24,0)';

// Uma projeção de taxa variável: 3 por cento, depois 3,5, depois 4
Sheet.Cells[6, 3].Formula := '=FVSCHEDULE(10000,{0.03;0.035;0.04})';

Validar uma implementação que não escreveu

As funções financeiras são a única área em que "devolve um número" não é prova de nada. O número é plausível por construção, e um erro numa convenção de contagem de dias desloca os resultados por uma quantidade demasiado pequena para se notar e demasiado grande para se aceitar

O teste que realmente funciona é um teste diferencial. Construa uma folha de cálculo de instrumentos representativos — uma obrigação por base de contagem de dias, uma que liquide no fim de um mês, uma que abranja um fevereiro de ano bissexto, um bilhete com menos de 182 dias e um com mais — calcule os valores no Excel uma vez, guarde-os como resultados esperados, e valide-os contra eles a cada atualização da biblioteca. As posições que atravessam uma data de cupão e as liquidações no dia 31 são onde as implementações divergem, por isso inclua ambas deliberadamente

Conjunto de validação diferencial para as funções de obrigações e tesouro do HotXLS em Delphi: instrumentos representativos, incluindo casos de ano bissexto e de fim de mês, são calculados uma vez no Excel e validados a cada atualização da biblioteca
Um conjunto diferencial avalia instrumentos representativos uma vez no Excel, guarda os valores como expectativas e valida-os a cada atualização do HotXLS, para que um desvio de pontos base não se consiga esconder atrás de um resultado plausível

Quanto aos próprios livros, os dois complementos desta funcionalidade são o motor de cálculo que decide o que recalcular, descrito em recálculo incremental e o grafo de dependências, e a família estatística abordada em funções de distribuição estatística. Juntas, são o que permite que um modelo financeiro corra sem supervisão num servidor, que foi toda a razão para as implementar nativamente

As finanças de obrigações, a estatística, as funções de engenharia e o motor de recálculo fazem parte da mesma biblioteca para Delphi e C++Builder; a lista completa de funcionalidades está disponível na página do componente de folha de cálculo HotXLS para Delphi