Strasmore Research
Обучение Matt ConnorАвтор: Matt Connor · data as of August 12, 2026 · refreshed weekly

libitofin: перенос QuantLib на Rust и работа с itofin

Библиотека libitofin переносит функционал QuantLib на язык Rust с доступом через Python. Узнайте, чем профессиональные инструменты оценки активов превосходят обычные таблицы.

libitofin можно описать одной фразой: это QuantLib на языке Rust. Проект представляет собой перенос QuantLib — библиотеки на C++, которая с начала двухтысячных годов является эталоном open source решений для оценки деривативов — на Rust, с Python-пакетом itofin в качестве надстройки. По состоянию на август 2026 года проект имеет статус pre-1.0, и этот факт определяет все практические аспекты его использования. Ценность библиотеки для оценки активов заключается в механизмах, окружающих формулы, и в любом языке программирования эта работа выглядит одинаково.

Что дает библиотека для оценки активов, чего нет в формулах электронной таблицы

Ячейка с формулой Black-Scholes в электронной таблице принимает пять входных параметров и возвращает цену. Сама формула — это самая простая часть. Вокруг неё выстраиваются четыре уровня, которые и составляют библиотеку.

Структуры процентных ставок (Term structures). Ставка дисконтирования — это кривая, зависящая от срока погашения, с правилом интерполяции для промежутков между точками, которые реально котируются на рынке. Ниже приведены исходные данные: котировки казначейских облигаций с разными сроками погашения на конкретную дату.

ЗапросКривая доходности казначейских облигаций на последнюю отчетную дату
Точный SQL-код для каждого числа
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 i
Run this yourself

По состоянию на Aug 10, 2026 кривая включала 7 сроков погашения, от 3.79% на уровне 1 month до 5.25% на уровне 30 years. В электронной таблице это решается поиском по таблице и жестко заданным значением в 4%. Библиотека же использует объект «кривая», на основе которого оцениваются все инструменты, с учетом заданного метода интерполяции (линейная по нулевым ставкам, логарифмически-линейная по дисконтным множителям, монотонные сплайны) и политики экстраполяции за пределами последней точки. Сдвиньте этот объект на один базисный пункт, и все показатели чувствительности в портфеле изменятся согласованно.

Конвенции исчисления дней (Day-count conventions). Проценты начисляются за часть года, и определение этой части — это конвенция, закрепленная за инструментом. Метод Actual/360 делит количество прошедших дней на 360. Actual/365 — на 365. Семейство 30/360 предполагает, что в каждом месяце 30 дней. Метод Business/252 учитывает торговые сессии относительно 252-дневного года, что требует наличия реального биржевого календаря с уже загруженными праздничными днями. Возьмем гипотетический миллион долларов, занятый под 5% на 90 дней: при методе actual/360 начислится 12 500 долларов, а при actual/365 — 12 329 долларов по той же сделке. Ниже показано, почему для базиса рабочих дней нужен календарь, а не просто делитель.

ЗапросТорговые сессии в соотношении с календарными днями по месяцам
Точный SQL-код для каждого числа
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 month
Run this yourself

За 12 месяцев, представленных на графике, 2026-07 насчитывал 22 торговых сессий против 31 календарных дней. Никакое арифметическое правило не даст это первое число. Оно берется из календаря праздников, и для каждого месяца ответ будет своим. Библиотека поставляет такие календари для каждой площадки и страны; в электронной таблице вам придется поддерживать их самостоятельно.

Механизм калибровки. Параметры модели нигде не котируются напрямую. Вы подбираете их, подгоняя цены модели под наблюдаемые рыночные цены по всей поверхности котировок, а затем пересчитываете их по мере движения рынка. Это задача минимизации методом наименьших квадратов с ограничениями, и библиотека предоставляет цикл для её решения: оптимизатор Левенберга-Марквардта, преобразования параметров для сохранения положительной дисперсии без жестких ограничений, целевую функцию для набора котировок и критерии сходимости, которые выдают ошибку, а не возвращают начальное приближение.

Уровень численных методов. В основе всего лежат линейная алгебра и интегрирование. QR- и SVD-разложения решают системы, возникающие при подгонке; квадратурные формулы и преобразование Фурье оценивают модели, формулы которых представляют собой интегралы; алгоритмы поиска корней вычисляют подразумеваемую волатильность. Этот уровень кажется скучным, но именно здесь чаще всего допускают ошибки при самостоятельной реализации. Классический пример — самописный решатель Ньютона, который сходится на ликвидных опционах «на деньгах» (at-the-money), но теряет точность на глубоко «вне денег» (out-of-the-money). Чуть менее опасная ошибка — обращение матрицы, теряющее точность при почти вырожденной системе и выдающее внешне правдоподобные, но неверные параметры. Если вы собираете модель с нуля, книга по количественной торговле с открытым исходным кодом будет лучшей отправной точкой, чем справочник API любой библиотеки.

Что такое libitofin и является ли это QuantLib на Rust?

Да, в важном смысле. Это перенос архитектуры QuantLib на Rust, и проект заявляет, что он протестирован с использованием собственного набора тестов QuantLib. Это честный способ портирования библиотеки численных методов: результаты проверяются по эталонной реализации, а не по субъективному ожиданию того, каким должен быть ответ. Для Python предусмотрен пакет itofin, поэтому процесс на Python может обращаться к движку на Rust без необходимости использования цепочки инструментов C++.

Более важный момент — статус pre-1.0. Согласно правилам версионирования Rust, релиз 0.x не дает гарантий совместимости между минорными версиями: в версии от 0.4 до 0.5 можно переименовать, переместить или удалить что угодно. Относитесь к API как к постоянно меняющейся цели, и следуйте нескольким правилам:

  • Фиксируйте точную версию релиза в файле зависимостей и обновляйтесь осознанно, используя собственный набор тестов как фильтр.
  • Создайте тонкую обертку над типами библиотеки, чтобы в случае переименования изменения нужно было вносить в один файл, а не в сорок.
  • Записывайте версию библиотеки рядом с полученными данными, чтобы при повторном запуске с другими результатами вы понимали, что дело в обновлении, а не в рынке.
  • Показатели маржи, обеспечения и регуляторные риски для продакшена держите на решениях, гарантирующих совместимость до выхода версии 1.0.

Это не критика проекта. Статус pre-1.0 — точное самоописание и правильное место для молодой версии очень большой библиотеки. Ошибка пользователя заключается в том, чтобы воспринимать её как готовую замену QuantLib и столкнуться с измененной сигнатурой в минорном релизе в середине квартала. Инструкции по установке также меняются вместе с версией, а cargo и pip требуют доступа к сети для разрешения зависимостей, поэтому читайте README проекта именно той версии, которую собираетесь зафиксировать, а не фрагменты из блогов, включая этот.

Python, скомпилированный движок или Rust?

Два вопроса решают всё, и ни один из них не касается личных предпочтений. Как часто в секунду вы пересчитываете цены и должны ли ваши цифры совпадать в разных процессах? Размер цепочки опционов определяет масштаб первого вопроса.

ЗапросКоличество уникальных опционных контрактов, проторгованных за сессию
Точный SQL-код для каждого числа
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 DESC
Run this yourself

На Aug 11, 2026, SPY показал 5336 различных контрактов в рамках одной сессии против 360 для KO. Оценить всю цепочку один раз — задача тривиальная. Оценивать её с пятью показателями чувствительности для каждого контракта при каждом обновлении котировок по всему портфелю базовых активов — это уже другая программа с другими ограничениями.

Оставайтесь в Python с проверенной библиотекой, если цикл оценки измеряется тысячами операций в минуту, а основная работа — это исследования, анализ или расчет цен на конец дня. Python-биндинги самого QuantLib — зрелый выбор: тот же движок на C++, широчайший охват инструментов и годы работы в продакшене. Скорость редко является критическим ограничением в исследовательском коде; важнее охват и корректность.

Вызывайте скомпилированный движок из Python, когда цикл оценки «горячий», а остальной код — нет. Главная издержка здесь — сам интерфейс взаимодействия. Вызов из Python для каждого контракта создает накладные расходы на каждом переходе, и решение заключается в передаче движку массива данных и получении массива обратно. Именно на эту нишу, наряду с QuantLib-Python, нацелен itofin.

Пишите на Rust, когда цикл оценки — это и есть продукт: прайсер внутри сервиса котирования, расчет рисков по жесткому графику или бинарный файл для системы, где нет Python. Второй вопрос также приводит к этому выбору. Результаты вычислений с плавающей запятой зависят от порядка операций, поэтому две реализации одной модели могут давать разные последние цифры, а расхождения между исследовательским блокнотом и продакшен-сервисом — это неделя работы по поиску причин. Один движок, используемый с обеих сторон, устраняет эту категорию несоответствий, что является веским аргументом в пользу скомпилированного ядра с биндингами, независимо от языка реализации. Тот же принцип применим к разработке стратегий, как показывает воспроизводимый бэктест.

Где на самом деле находится цель калибровки

Для калибровки нужно что-то, под что можно подстраиваться, и это «что-то» — поверхность подразумеваемой рыночной волатильности. Сторона поиска корней для этого описана в том, как рассчитывается подразумеваемая волатильность. Форма ниже — это то, чему должна соответствовать модель.

ЗапросВременная структура подразумеваемой волатильности AAPL (около денег)
Точный SQL-код для каждого числа
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)
Run this yourself

Вблизи цены исполнения контракты AAPL в корзине 0 to 7 days имели среднюю подразумеваемую волатильность 29.6% за рассматриваемые сессии против 29.3% на 241 days or more. Модель с одним параметром волатильности не может соответствовать обеим точкам одновременно, именно поэтому существуют модели со структурой волатильности. Подгонка модели к такой поверхности — это этап калибровки, а чувствительности, которые вытекают из подогнанной модели, — это «греки», описанные в объяснении греков опционов.

Как были построены эти панели

Панель кривых считывает последнюю котируемую дату в серии казначейских облигаций и разворачивает одиннадцать столбцов сроков погашения в строки, отбрасывая сроки без котировок на эту дату. Панель сессий подсчитывает уникальные даты в ленте SPY по месяцам, что является надежным индикатором количества полноценных торговых сессий. Панель цепочек считает уникальные коды контрактов с ненулевым объемом на последней доступной сессии, сгруппированные по базовому активу. Панель волатильности сохраняет только успешные расчеты с ненулевым объемом и ценами исполнения в пределах 5% от цены закрытия базового актива, что является стандартным диапазоном «около денег»; передняя корзина включает опционы с истечением в тот же день.

Часто задаваемые вопросы

Является ли libitofin прямой заменой QuantLib?

Нет. По состоянию на август 2026 года это порт pre-1.0, который покрывает часть функционала QuantLib и проверяет свои результаты по собственному набору тестов QuantLib. Сам QuantLib, доступный через Python-биндинги, остается более широким и стабильным вариантом для продакшена.

Что означает pre-1.0 для библиотеки оценки активов?

Согласно правилам версионирования Rust, релиз 0.x не дает гарантий совместимости: следующая минорная версия может переименовать или удалить что угодно. На практике это означает фиксацию точной версии релиза и повторный запуск собственных тестов при каждом обновлении.

Нужно ли мне писать на Rust, чтобы использовать libitofin?

Нет. Проект публикует Python-пакет itofin, поэтому движок можно вызывать из обычного процесса на Python. Написание кода на Rust становится актуальным, когда сам цикл оценки является продуктом, который вы поставляете.

Что дает библиотека для оценки, чего нет в формулах электронной таблицы?

Кривые вместо одиночных ставок, конвенции исчисления дней с реальными биржевыми календарями, цикл калибровки, подгоняющий параметры модели под котируемые цены, и протестированный уровень численных методов в основе всего этого. Формула в замкнутом виде — лишь малая часть работы.

Меняет ли язык программирования цену опциона?

Математически — нет. Он меняет воспроизводимость: результаты вычислений с плавающей запятой зависят от порядка операций, поэтому две реализации одной модели могут расходиться в последних цифрах. Использование одного движка для исследований и продакшена устраняет этот разрыв.


Каждая панель здесь содержит свой SQL-запрос, поэтому разверните любую из них, чтобы увидеть, как проводился расчет. Те же вопросы по кривым, календарям и цепочкам можно задать на обычном английском языке в терминале Strasmore.