QuantLib em Rust: conheça a biblioteca libitofin
A libitofin traz a QuantLib para Rust com interface Python via itofin. Entenda por que usar bibliotecas de precificação em vez de planilhas e a importância de versões.
QuantLib em Rust é a descrição resumida da libitofin: uma portabilidade da QuantLib, a biblioteca C++ que tem sido a referência em código aberto para precificação de derivativos desde o início dos anos dois mil, para a linguagem Rust, com um pacote Python chamado itofin sobreposta a ela. Em agosto de 2026, o projeto descreve-se como pré-1.0, e esse rótulo rege todos os aspectos práticos de sua utilização. Uma biblioteca de precificação conquista seu espaço por meio da estrutura montada ao redor da fórmula, e essa estrutura é o mesmo trabalho em qualquer linguagem.
O que uma biblioteca de precificação oferece que uma fórmula de planilha não oferece
Uma célula de Black-Scholes em uma planilha recebe cinco entradas e retorna um preço. A fórmula é a parte fácil. Quatro camadas a envolvem, e essas camadas constituem a biblioteca.
Estruturas a termo. Uma taxa de desconto é uma curva ao longo dos vencimentos, com uma regra de interpolação para os intervalos entre os pontos que alguém realmente cota. O painel abaixo apresenta a matéria-prima: tenores de títulos do Tesouro cotados em uma única data.
O SQL exato por trás de cada número
SELECT
arrayElement(tenors, i) AS tenor,
round(arrayElement(rates, i), 2) AS yield_pct,
formatDateTime(curve_date, '%b %e, %Y') AS as_of
FROM
(
SELECT
date AS curve_date,
['1 month', '3 months', '6 months', '1 year', '2 years', '3 years',
'5 years', '7 years', '10 years', '20 years', '30 years'] AS tenors,
[toFloat64(yield_1_month), toFloat64(yield_3_month), toFloat64(yield_6_month),
toFloat64(yield_1_year), toFloat64(yield_2_year), toFloat64(yield_3_year),
toFloat64(yield_5_year), toFloat64(yield_7_year), toFloat64(yield_10_year),
toFloat64(yield_20_year), toFloat64(yield_30_year)] AS rates,
arrayJoin(range(1, 12)) AS i
FROM global_markets.treasury_yields
WHERE date = (SELECT max(date) FROM global_markets.treasury_yields)
)
WHERE yield_pct > 0
ORDER BY iEm Aug 10, 2026, a curva cotada apresentava 7 tenores, variando de 3.79% a 1 month até 5.25% a 30 years. Uma planilha resolve isso com uma busca e uma taxa fixa de quatro por cento. Uma biblioteca resolve com um objeto de curva que precifica todos os instrumentos, sob uma interpolação definida (linear em taxas zero, log-linear em fatores de desconto, splines monótonas) e uma política de extrapolação definida após o último ponto. Desloque esse objeto em um ponto-base e todas as sensibilidades na carteira se movem de forma consistente com ele.
Convenções de contagem de dias. Os juros acumulam-se sobre uma fração de um ano, e a definição dessa fração é uma convenção vinculada ao instrumento. Actual/360 divide os dias decorridos por trezentos e sessenta. Actual/365 divide por trezentos e sessenta e cinco. A família 30/360 finge que todo mês tem trinta dias. Business/252 conta as sessões de negociação em relação a um ano de duzentos e cinquenta e dois dias, o que exige um calendário de bolsa real com os feriados já carregados. Considere um milhão de dólares hipotético emprestado a cinco por cento por noventa dias: o método actual/360 acumula doze mil e quinhentos dólares e o actual/365 acumula doze mil trezentos e vinte e nove dólares na mesma operação. O painel abaixo mostra por que a base de dias úteis precisa de um calendário em vez de um divisor.
O SQL exato por trás de cada número
SELECT
formatDateTime(toStartOfMonth(date), '%Y-%m') AS month,
countDistinct(date) AS trading_days,
toUInt8(toDayOfMonth(toLastDayOfMonth(max(date)))) AS calendar_days
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
AND date >= toStartOfMonth(today() - 365)
AND date < toStartOfMonth(today())
GROUP BY month
ORDER BY monthAo longo dos 12 meses em análise, 2026-07 registrou 22 sessões contra 31 dias corridos. Nenhuma regra aritmética produz esse primeiro número. Ele vem de um calendário de feriados, e cada mês tem sua própria resposta. Uma biblioteca fornece esses calendários por local e por país; uma planilha exige que você os mantenha.
Estrutura de calibração. Os parâmetros do modelo não são cotados em lugar nenhum. Você os escolhe ajustando os preços do modelo aos preços de mercado observados em toda uma superfície de cotações, e então reajusta conforme a superfície se move. Esse é um problema de mínimos quadrados limitados, e a biblioteca fornece o ciclo ao redor dele: um otimizador de Levenberg-Marquardt, transformações de parâmetros que mantêm a variância positiva sem restrições rígidas, uma função de custo sobre o conjunto de cotações e critérios de convergência que falham de forma clara em vez de retornar silenciosamente o palpite inicial.
Uma camada numérica. Abaixo de tudo, encontram-se a álgebra linear e a integração. Decomposições QR e SVD resolvem os sistemas que um ajuste produz, quadratura e integração de Fourier precificam os modelos cujas fórmulas são integrais, e localizadores de raízes extraem a volatilidade implícita. Essa camada é monótona, e é exatamente a camada que é reimplementada de forma precária. A versão clássica é um solver de Newton manual que converge em cotações líquidas no dinheiro (at-the-money) e se perde em cotações muito fora do dinheiro (out-of-the-money). Em segundo lugar, está uma inversão de matriz que perde precisão em um ajuste quase singular e devolve parâmetros que parecem totalmente plausíveis. Se você está montando o modelo mental do zero, um livro de trading quantitativo de código aberto é um ponto de partida melhor do que a referência de API de qualquer biblioteca.
O que é a libitofin e ela é a QuantLib em Rust?
Sim, no sentido que importa. É uma portabilidade do design da QuantLib para Rust, e o projeto afirma que é testado contra a própria suíte de testes da QuantLib. Essa é a forma honesta de portar uma biblioteca numérica: os resultados são verificados contra a implementação de referência em vez de uma expectativa escrita à mão sobre qual deveria ser a resposta. O caminho para Python é um pacote chamado itofin, para que um processo Python possa acessar o motor Rust sem a necessidade de uma cadeia de ferramentas C++ no processo.
O rótulo que importa mais é o pré-1.0. Sob a convenção de versionamento do Rust, um lançamento 0.x não carrega promessa de compatibilidade entre versões menores: de 0.4 para 0.5 é permitido renomear, mover ou excluir qualquer coisa. Trate a API como um alvo móvel, e alguns hábitos devem ser seguidos.
- Fixe uma versão marcada exata no seu arquivo de bloqueio (lockfile) e atualize intencionalmente, usando sua própria suíte de testes como filtro.
- Mantenha um wrapper próprio e leve ao redor dos tipos da biblioteca, para que uma renomeação ocorra em um arquivo em vez de quarenta.
- Registre a versão da biblioteca ao lado dos números que ela produziu, para que uma reexecução que discorde aponte para a atualização em vez de para o mercado.
- Mantenha os números de margem de produção, colateral e risco regulatório em algo que carregue uma promessa de compatibilidade até que a versão 1.0 chegue.
Nada disso é uma crítica ao projeto. Pré-1.0 é uma autodescrição precisa e o lugar certo para uma portabilidade jovem de uma biblioteca muito grande estar. O modo de falha é um leitor tratá-la como uma substituta direta da QuantLib e encontrar uma assinatura alterada em uma versão menor, no meio de um trimestre. As instruções de instalação também mudam com a versão, e tanto o cargo quanto o pip precisam de acesso à rede para resolver qualquer coisa, portanto, leia o próprio README do projeto na tag que você pretende fixar em vez de um trecho copiado de um post de blog, este incluído.
Python, um motor compilado ou Rust?
Duas perguntas resolvem isso, e nenhuma delas é sobre preferência. Quantas vezes por segundo você repricifica, e seus números precisam coincidir entre processos separados? O tamanho da cadeia define a escala da primeira pergunta.
O SQL exato por trás de cada número
WITH (SELECT max(date) FROM global_markets.options_greeks) AS last_session
SELECT
underlying_symbol AS symbol,
countDistinct(ticker) AS contracts_priced,
formatDateTime(max(date), '%b %e, %Y') AS as_of
FROM global_markets.options_greeks
WHERE date = last_session
AND underlying_symbol IN ('SPY', 'AAPL', 'NVDA', 'MSFT', 'KO')
AND volume > 0
GROUP BY symbol
ORDER BY contracts_priced DESCEm Aug 11, 2026, SPY teve 5336 contratos distintos negociados em uma única sessão, contra 360 para KO. Precificar a cadeia ampla uma vez não é nada. Precificá-la com cinco sensibilidades por contrato, a cada atualização de cotação, em uma carteira de ativos subjacentes, é um programa diferente com restrições diferentes.
Permaneça em Python com uma biblioteca estabelecida quando o ciclo for medido em milhares de avaliações por minuto e o trabalho ao redor for pesquisa, análise ou marcação de fim de dia. As próprias ligações (bindings) Python da QuantLib são a escolha madura: o mesmo motor C++, a maior cobertura de instrumentos e anos de uso em produção. A velocidade raramente é a restrição limitante em código de pesquisa; cobertura e correção são.
Chame um motor compilado a partir do Python quando o ciclo for intenso e o código ao redor não for. O custo a observar é a própria fronteira. Uma chamada por contrato a partir do Python paga um custo operacional (overhead) a cada cruzamento, e a solução é entregar ao motor um array e receber um array de volta. Este é o lugar que a itofin visa ocupar, ao lado da QuantLib-Python.
Escreva em Rust quando o ciclo de precificação for o produto: um precificador dentro de um serviço de cotação, uma execução de risco em um cronograma que você não pode perder, ou um binário enviado para uma máquina sem Python. A segunda pergunta também se aplica aqui. Resultados de ponto flutuante dependem da ordem das operações, portanto, o mesmo modelo implementado duas vezes pode discordar nos últimos dígitos, e um notebook de pesquisa que discorda de um serviço de produção representa uma semana de perícia. Um único motor usado de ambos os lados remove toda essa categoria de discrepância, que é o argumento durável para um núcleo compilado com ligações, independentemente da linguagem em que esteja escrito. O mesmo instinto se aplica ao trabalho de estratégia, como mostra um backtest reprodutível.
Onde o alvo de calibração realmente reside
A calibração precisa de algo contra o qual se ajustar, e esse algo é uma superfície de volatilidades implícitas de mercado. O lado da localização de raízes disso está coberto em como a volatilidade implícita é calculada. A forma abaixo é o que um modelo precisa igualar.
O SQL exato por trás de cada número
SELECT
multiIf(days_to_expiry <= 7, '0 to 7 days',
days_to_expiry <= 30, '8 to 30 days',
days_to_expiry <= 60, '31 to 60 days',
days_to_expiry <= 120, '61 to 120 days',
days_to_expiry <= 240, '121 to 240 days',
'241 days or more') AS dte_bucket,
round(avg(implied_volatility) * 100, 1) AS iv_pct,
countDistinct(ticker) AS contracts
FROM global_markets.options_greeks
WHERE underlying_symbol = 'AAPL'
AND date >= today() - 10
AND days_to_expiry >= 0
AND iv_converged = 1
AND volume > 0
AND abs(toFloat64(strike_price) / toFloat64(underlying_close) - 1) < 0.05
GROUP BY dte_bucket
ORDER BY min(days_to_expiry)Perto do dinheiro (near the money), os contratos de AAPL no balde 0 to 7 days tiveram uma volatilidade implícita média de 29.6% ao longo das sessões em análise, contra 29.3% em 241 days or more. Um modelo que carrega um único parâmetro de volatilidade não pode estar em ambos os pontos ao mesmo tempo, que é a razão pela qual existem modelos com uma estrutura a termo de volatilidade. Ajustar um a uma superfície como esta é o passo de calibração, e as sensibilidades que derivam do modelo ajustado são as gregas, cobertas em as gregas das opções explicadas.
Como estes painéis foram construídos
O painel de curva lê a data cotada mais recente na série do Tesouro e desenrola suas onze colunas de tenor em linhas, descartando qualquer tenor sem cotação naquele dia. O painel de sessão conta datas distintas na fita (tape) do SPY por mês, o que é um indicador limpo para uma contagem completa de sessões de bolsa. O painel de cadeia conta códigos de contrato distintos com volume não nulo na última sessão disponível, agrupados por subjacente. O painel de volatilidade mantém apenas resoluções convergentes com volume não nulo e strikes dentro de cinco por cento do fechamento do subjacente, que é a banda padrão próxima ao dinheiro; o balde frontal inclui vencimentos no mesmo dia.
Perguntas frequentes
A libitofin é uma substituta direta para a QuantLib?
Não. Em agosto de 2026, é uma portabilidade pré-1.0 que cobre parte da superfície da QuantLib e valida seus resultados contra a própria suíte de testes da QuantLib. A própria QuantLib, acessada por meio de suas ligações Python, continua sendo a opção mais ampla e estável para trabalhos de produção.
O que significa pré-1.0 para uma biblioteca de precificação?
Sob a convenção de versionamento do Rust, um lançamento 0.x não faz promessa de compatibilidade: a próxima versão menor é livre para renomear ou remover qualquer coisa. Na prática, isso significa fixar uma versão marcada exata e reexecutar sua própria suíte de testes a cada atualização.
Preciso escrever em Rust para usar a libitofin?
Não. O projeto publica um pacote Python chamado itofin, portanto, o motor é chamável a partir de um processo Python normal. Escrever em Rust torna-se relevante quando o próprio ciclo de precificação é o que você está entregando.
O que uma biblioteca de precificação me dá que uma fórmula de planilha não dá?
Curvas em vez de taxas únicas, convenções de contagem de dias com calendários de bolsa reais por trás, um ciclo de calibração que ajusta os parâmetros do modelo aos preços cotados e uma camada numérica testada abaixo de todos os três. A fórmula de forma fechada é a pequena parte do trabalho.
A linguagem de programação altera o preço da opção?
Matematicamente, não. Ela altera a reprodutibilidade: resultados de ponto flutuante dependem da ordem das operações, portanto, duas implementações de um modelo podem discordar nos dígitos finais. Executar pesquisa e produção a partir do mesmo motor remove essa lacuna.
Cada painel aqui carrega seu SQL exato por baixo, então expanda um para ver como a contagem foi feita. As mesmas perguntas sobre curva, calendário e cadeia podem ser feitas em inglês simples no terminal Strasmore.