Qué es libitofin: la adaptación de QuantLib a Rust
Analizamos libitofin como puerto de QuantLib en Rust y su integración con Python mediante itofin. Evaluamos por qué usar una biblioteca de valoración frente a hojas de cálculo.
QuantLib en Rust es la descripción breve de libitofin: una adaptación a Rust de QuantLib, la biblioteca de C++ que ha sido la referencia de código abierto para la valoración de derivados desde principios de la década de los dos mil, con un paquete de Python llamado itofin como capa superior. A agosto de 2026, el proyecto se define a sí mismo como pre-1.0, y esa etiqueta rige todo lo práctico sobre su uso. Una biblioteca de valoración se gana su lugar gracias a la maquinaria que envuelve a la fórmula, y esa maquinaria es el mismo trabajo en cualquier lenguaje.
Lo que ofrece una biblioteca de valoración que no proporciona una fórmula de hoja de cálculo
Una celda de Black-Scholes en una hoja de cálculo toma cinco entradas y devuelve un precio. La fórmula es la parte sencilla. Cuatro capas la rodean, y esas capas son la biblioteca.
Estructuras de plazos (Term structures). Una tasa de descuento es una curva a través de vencimientos, con una regla de interpolación para los huecos entre los puntos que alguien cotiza realmente. El panel a continuación muestra la materia prima: tenores del Tesoro cotizados en una fecha única.
El SQL exacto detrás de cada cifra
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 iA fecha de Aug 10, 2026, la curva cotizada presentaba 7 tenores, desde 3.79% al 1 month hasta 5.25% al 30 years. Una hoja de cálculo resuelve esto con una búsqueda y un 4% codificado. Una biblioteca lo resuelve con un objeto de curva sobre el cual cada instrumento calcula su precio, bajo una interpolación establecida (lineal sobre tasas cero, log-lineal sobre factores de descuento, splines monótonos) y una política de extrapolación definida más allá del último punto. Desplace ese objeto un punto básico y cada sensibilidad en el libro se moverá de forma consistente con él.
Convenciones de conteo de días (Day-count conventions). El interés se devenga sobre una fracción de año, y la definición de esa fracción es una convención vinculada al instrumento. Actual/360 divide los días transcurridos entre 360. Actual/365 divide entre 365. La familia 30/360 finge que cada mes tiene 30 días. Business/252 cuenta las sesiones de negociación frente a un año de 252 días, lo que requiere un calendario bursátil real con los días festivos ya cargados. Tome un millón de dólares hipotético prestado al 5% durante 90 días: actual/360 devenga 12.500 dólares y actual/365 devenga 12.329 dólares en la misma operación. El panel a continuación muestra por qué la base de días hábiles necesita un calendario en lugar de un divisor.
El SQL exacto detrás de cada cifra
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 monthDurante los 12 meses analizados, 2026-07 mantuvo 22 sesiones frente a 31 días naturales. Ninguna regla aritmética produce ese primer número. Proviene de un calendario de festivos, y cada mes tiene su propia respuesta. Una biblioteca incluye esos calendarios por mercado y por país; una hoja de cálculo le pide que usted los mantenga.
Maquinaria de calibración. Los parámetros del modelo no se cotizan en ninguna parte. Usted los elige ajustando los precios del modelo a los precios de mercado observados a través de toda una superficie de cotizaciones, y luego reajustándolos a medida que la superficie se mueve. Ese es un problema de mínimos cuadrados acotados, y la biblioteca suministra el bucle necesario: un optimizador de Levenberg-Marquardt, transformaciones de parámetros que mantienen una varianza positiva sin restricciones rígidas, una función de coste sobre el conjunto de cotizaciones y criterios de convergencia que fallan de forma evidente en lugar de devolver silenciosamente la estimación inicial.
Una capa numérica. Debajo de todo se encuentra el álgebra lineal y la integración. Las descomposiciones QR y SVD resuelven los sistemas que produce un ajuste, la cuadratura y la integración de Fourier valoran los modelos cuyas fórmulas son integrales, y los buscadores de raíces extraen la volatilidad implícita. Esta capa es tediosa, y es exactamente la capa que se suele reimplementar mal. La versión clásica es un solucionador de Newton hecho a mano que converge en cotizaciones líquidas at-the-money y se desvía en las deep out-of-the-money. Un segundo error común es una inversa de matriz que pierde precisión en un ajuste casi singular y devuelve parámetros que parecen totalmente plausibles. Si está construyendo el modelo mental desde cero, un libro de trading cuantitativo de código abierto es un mejor punto de partida que la referencia de API de cualquier biblioteca.
¿Qué es libitofin y es QuantLib en Rust?
Sí, en el sentido que importa. Es una adaptación del diseño de QuantLib a Rust, y el proyecto declara que está probado contra el propio conjunto de pruebas de QuantLib. Esa es la forma honesta de adaptar una biblioteca numérica: los resultados se verifican contra la implementación de referencia en lugar de contra una expectativa escrita a mano de cuál debería ser la respuesta. La ruta de Python es un paquete llamado itofin, por lo que un proceso de Python puede acceder al motor de Rust sin necesidad de una cadena de herramientas de C++ en el bucle.
La etiqueta que importa más es pre-1.0. Bajo la convención de versiones de Rust, un lanzamiento 0.x no conlleva ninguna promesa de compatibilidad entre versiones menores: de 0.4 a 0.5 se permite renombrar, mover o eliminar cualquier cosa. Trate la API como un objetivo móvil, y se derivan algunos hábitos.
- Fije una versión etiquetada exacta en su archivo de bloqueo (lockfile) y actualice a propósito, utilizando su propio conjunto de pruebas como filtro.
- Mantenga un envoltorio ligero propio alrededor de los tipos de la biblioteca, para que un cambio de nombre afecte a un solo archivo en lugar de a cuarenta.
- Registre la versión de la biblioteca junto a los números que produjo, de modo que una nueva ejecución que no coincida apunte a la actualización y no al mercado.
- Mantenga los números de margen de producción, colateral y riesgo regulatorio en algo que lleve una promesa de compatibilidad hasta que llegue una versión 1.0.
Nada de esto es una crítica al proyecto. Pre-1.0 es una autodescripción precisa y el lugar correcto para una adaptación joven de una biblioteca muy grande. El modo de fallo es que un lector la trate como un reemplazo directo de QuantLib y se encuentre con una firma cambiada en una versión menor, a mitad de un trimestre. Las instrucciones de instalación también cambian con la versión, y tanto cargo como pip necesitan acceso a la red para resolver cualquier cosa, así que lea el propio README del proyecto en la etiqueta que pretende fijar en lugar de un fragmento copiado de una entrada de blog, incluida esta.
¿Python, un motor compilado o Rust?
Dos preguntas resuelven esto, y ninguna trata sobre gustos. ¿Cuántas veces por segundo valora, y sus números deben coincidir entre procesos separados? El tamaño de la cadena (chain size) establece la escala de la primera pregunta.
El SQL exacto detrás de cada cifra
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 DESCEl Aug 11, 2026, SPY tuvo 5336 contratos distintos negociados en una sola sesión, frente a 360 para KO. Valorar la cadena amplia una vez no es nada. Valorarla con cinco sensibilidades por contrato, en cada actualización de cotización, a través de una cartera de subyacentes, es un programa diferente con restricciones diferentes.
Permanezca en Python con una biblioteca establecida cuando el bucle se mida en miles de valoraciones por minuto y el trabajo circundante sea investigación, análisis o marcas de fin de día. Los enlaces de Python de QuantLib son la opción madura: el mismo motor de C++, la mayor cobertura de instrumentos y años de uso en producción. La velocidad rara vez es la restricción vinculante en el código de investigación; la cobertura y la precisión sí lo son.
Llame a un motor compilado desde Python cuando el bucle sea intensivo y el código que lo rodea no lo sea. El coste a vigilar es el límite mismo. Una llamada por contrato desde Python paga una sobrecarga en cada cruce, y la solución es entregar al motor una matriz y recibir una matriz de vuelta. Este es el nicho al que apunta itofin, junto a QuantLib-Python.
Escriba en Rust cuando el bucle de valoración sea el producto: un valorador dentro de un servicio de cotización, un cálculo de riesgo en un horario que no puede fallar, o un binario enviado a una máquina sin Python. La segunda pregunta también aterriza aquí. Los resultados de punto flotante dependen del orden de las operaciones, por lo que el mismo modelo implementado dos veces puede discrepar en los últimos dígitos, y un cuaderno de investigación que discrepa con un servicio de producción supone una semana de análisis forense. Un mismo motor utilizado desde ambos lados elimina toda esa categoría de discrepancia, que es el argumento duradero para un núcleo compilado con enlaces, independientemente del lenguaje en el que esté escrito. El mismo instinto se aplica al trabajo de estrategia, como muestra un backtest reproducible.
Dónde reside realmente el objetivo de calibración
La calibración necesita algo contra lo que ajustarse, y ese algo es una superficie de volatilidades implícitas de mercado. El lado de la búsqueda de raíces de eso se cubre en cómo se calcula la volatilidad implícita. La forma a continuación es lo que un modelo debe igualar.
El SQL exacto detrás de cada cifra
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)Cerca del dinero (near the money), los contratos de AAPL en el segmento 0 to 7 days promediaron 29.6% de volatilidad implícita a través de las sesiones analizadas, frente a 29.3% al 241 days or more. Un modelo que lleva un parámetro de volatilidad no puede situarse en ambos puntos a la vez, que es la razón por la que existen modelos con una estructura de plazos de volatilidad. Ajustar uno a una superficie como esta es el paso de calibración, y las sensibilidades que se derivan del modelo ajustado son las griegas, cubiertas en las griegas de las opciones explicadas.
Cómo se construyeron estos paneles
El panel de curvas lee la fecha cotizada más reciente en la serie del Tesoro y despliega sus once columnas de tenor en filas, descartando cualquier tenor sin cotización ese día. El panel de sesiones cuenta las fechas distintas en la cinta de SPY por mes, lo cual es un indicador limpio para un conteo completo de sesiones bursátiles. El panel de cadenas cuenta los códigos de contrato distintos con volumen distinto de cero en la última sesión disponible, agrupados por subyacente. El panel de volatilidad mantiene solo las soluciones convergentes con volumen distinto de cero y strikes dentro del 5% del cierre del subyacente, que es la banda estándar near-the-money; el segmento frontal incluye vencimientos del mismo día.
Preguntas frecuentes
¿Es libitofin un reemplazo directo de QuantLib?
No. A agosto de 2026 es una adaptación pre-1.0 que cubre parte de la superficie de QuantLib y valida sus resultados contra el propio conjunto de pruebas de QuantLib. QuantLib en sí, al que se accede a través de sus enlaces de Python, sigue siendo la opción más amplia y estable para el trabajo de producción.
¿Qué significa pre-1.0 para una biblioteca de valoración?
Bajo la convención de versiones de Rust, un lanzamiento 0.x no hace ninguna promesa de compatibilidad: la siguiente versión menor es libre de renombrar o eliminar cualquier cosa. En la práctica, eso significa fijar una versión etiquetada exacta y volver a ejecutar su propio conjunto de pruebas en cada actualización.
¿Necesito escribir en Rust para usar libitofin?
No. El proyecto publica un paquete de Python llamado itofin, por lo que el motor es invocable desde un proceso normal de Python. Escribir en Rust se vuelve relevante cuando el bucle de valoración en sí es lo que usted está distribuyendo.
¿Qué me ofrece una biblioteca de valoración que no me da una fórmula de hoja de cálculo?
Curvas en lugar de tasas únicas, convenciones de conteo de días con calendarios bursátiles reales detrás, un bucle de calibración que ajusta los parámetros del modelo a los precios cotizados y una capa numérica probada debajo de los tres. La fórmula de forma cerrada es la parte pequeña del trabajo.
¿Cambia el lenguaje de programación el precio de la opción?
No matemáticamente. Cambia la reproducibilidad: los resultados de punto flotante dependen del orden de las operaciones, por lo que dos implementaciones de un mismo modelo pueden discrepar en los dígitos finales. Ejecutar la investigación y la producción desde el mismo motor elimina esa brecha.
Cada panel aquí lleva su SQL exacto debajo, así que expanda uno para ver cómo se tomó el conteo. Las mismas preguntas sobre curvas, calendarios y cadenas pueden hacerse en inglés sencillo en la terminal Strasmore.