Artigo Técnico

Fórmulas de títulos e tesouraria no Delphi com o HotXLS

O HotXLS avalia nativamente as funções financeiras de títulos e tesouraria do Excel no Delphi e no C++Builder: PRICE, YIELD, DURATION, MDURATION, as seis primitivas de data de cupom, o trio de letras do tesouro, a família de desconto e as funções de depreciação francesas. Elas são calculadas dentro do próprio motor de fórmulas da biblioteca, de modo que uma pasta de trabalho que precifica uma carteira de títulos recalcula em um servidor sem Excel e sem o suplemento Analysis ToolPak em lugar nenhum à vista

Essas funções costumavam viver no Analysis ToolPak, razão pela qual ainda carregam uma reputação de serem incômodas. Desde o Excel 2007 elas fazem parte do conjunto de funções padrão e são gravadas sem adornos no formato de arquivo, sem o prefixo _xlfn. que funções genuinamente mais novas carregam. Uma pasta de trabalho que as usa é uma pasta de trabalho comum; a única questão é se o que quer que a recalcule conhece as funções

O que o motor de datas de cupom precisa acertar

Todo cálculo de título repousa sobre as mesmas primitivas: quantos dias desde o último cupom, quantos dias no período de cupom atual, quantos dias até o próximo cupom, quando esses cupons caem e quantos restam. Essas são COUPDAYBS, COUPDAYS, COUPDAYSNC, COUPPCD, COUPNCD e COUPNUM, e toda função de preço e de rendimento as chama

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

O HotXLS implementa as cinco em um motor aninhado autocontido de datas de cupom, compartilhado por toda função que precisa dele, de modo que PRICE e DURATION não podem discordar entre si sobre onde ficam as datas de cupom

Motor de datas de cupom do HotXLS para Delphi: as entradas do título e a base de contagem de dias selecionada alimentam seis primitivas COUP compartilhadas por PRICE, YIELD, DURATION e MDURATION
As cinco bases de contagem de dias alimentam um único motor aninhado de datas de cupom compartilhado por toda função financeira do HotXLS, de modo que PRICE e DURATION nunca podem discordar sobre onde caem as datas de cupom

Precificação e rendimento em uma pasta de trabalho

Nada no código de chamada é especial: escreva as fórmulas, recalcule, leia os valores. Esse é o ponto — a pasta de trabalho é o modelo, e seu aplicativo 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);  // vencimento
    Sheet.Cells[3, 1].Value := 0.0425;                    // taxa de cupom
    Sheet.Cells[4, 1].Value := 0.0391;                    // rendimento exigido
    Sheet.Cells[5, 1].Value := 100;                       // resgate
    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, não como strings. Uma data em texto funciona no Excel porque o Excel a converte, e a conversão depende do idioma regional, 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 ficam por baixo são abordadas em números de série de data e o sistema 1904

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

PRICE é um cálculo direto: desconte os fluxos de caixa, some-os. YIELD é seu inverso e não tem solução de forma fechada para um título com mais de um cupom restante, então precisa ser resolvido numericamente. O HotXLS usa um resolvedor por bisseção, que converge de forma confiável, embora não rapidamente

Duas implicações práticas decorrem disso. Yield é mensuravelmente mais caro do que price, de modo que uma revalorização de carteira que calcula rendimentos para dez mil posições custa perceptivelmente mais do que uma que calcula preços, e vale a pena saber qual dos dois seu modelo realmente precisa. E um rendimento que não pode ser delimitado — porque as entradas descrevem um título que nenhum rendimento consegue precificar no valor dado — retorna um erro em vez de um número errado, o que é o comportamento correto e o que se deve testar

No HotXLS em Delphi, PRICE é uma única passagem direta de fluxo de caixa descontado, enquanto YIELD não tem forma fechada e é resolvido por um loop de bisseção que retorna um erro quando não existe um intervalo de busca
PRICE é uma única passagem direta sobre os fluxos de caixa descontados, enquanto YIELD não tem forma fechada e é delimitado e bissectado, retornando um erro em vez de um número errado quando as entradas não podem ser precificadas

Letras do tesouro são sua própria família

As letras são instrumentos de desconto sem cupons, então usam TBILLPRICE, TBILLYIELD e TBILLEQ em vez das funções de título. TBILLEQ converte uma taxa de desconto em um rendimento equivalente a título, e tem uma peculiaridade documentada que vale a pena conhecer: para vencimentos de mais de 182 dias, a conversão é quadrática em vez de linear, porque o título abrange mais de um período de meio ano

Alimentar uma letra em PRICE em vez disso, com cupom 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 esses instrumentos são cotados e liquidados de forma diferente

O restante do conjunto, em um só lugar

Além de títulos e letras, o mesmo lote cobre as funções que os cercam em modelos reais. PRICEMAT e YIELDMAT tratam de títulos que pagam juros no vencimento. ACCRINTM calcula os juros acumulados para eles. EFFECT e NOMINAL convertem entre taxas anuais efetiva e nominal, a conversão mais comumente feita errada à mão. CUMIPMT e CUMPRINC dão juros e principal cumulativos 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ólar decimal e fracionária, 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, exigidos pelas regras contábeis francesas, não conveniências opcionais

// Uma conversão de taxa que as pessoas rotineiramente erram à 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 cumulativos nos dois primeiros 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})';

Validando uma implementação que você não escreveu

Funções financeiras são a única área em que "retorna um número" não é evidência de nada. O número é plausível por construção, e um erro em uma convenção de contagem de dias desloca os resultados por uma quantidade pequena demais para notar e grande demais para aceitar

O teste que realmente funciona é diferencial. Monte uma planilha de instrumentos representativos — um título por base de contagem de dias, um que liquida no fim de um mês, um que abrange um fevereiro de ano bissexto, uma letra abaixo de 182 dias e uma acima — calcule os valores no Excel uma vez, armazene-os como resultados esperados, e verifique-os contra eles após toda atualização da biblioteca. Posições que atravessam uma data de cupom e liquidações no dia 31 são onde as implementações divergem, então inclua ambas deliberadamente

Conjunto de validação diferencial para as funções de títulos e tesouraria do HotXLS no Delphi: instrumentos representativos, incluindo casos de ano bissexto e fim de mês, são calculados uma vez no Excel e verificados após toda atualização da biblioteca
Um conjunto diferencial precifica instrumentos representativos uma vez no Excel, armazena os valores como expectativas e os verifica após toda atualização do HotXLS, de modo que um desvio de pontos-base não pode se esconder atrás de uma saída plausível

Para as próprias pastas de trabalho, as duas companheiras deste recurso 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 rode sem supervisão em um servidor, que foi todo o motivo para implementá-las nativamente

Finanças de títulos, estatística, funções de engenharia e o motor de recálculo vêm em uma única biblioteca para Delphi e C++Builder; a lista completa de recursos está na página do componente de planilha Delphi HotXLS