Strasmore Research
Навчання Matt ConnorВід Matt Connor · data as of August 12, 2026 · refreshed weekly

QuantLib на Rust: огляд бібліотеки libitofin

Дізнайтеся про портування QuantLib на мову Rust через бібліотеку libitofin. Чому професійні інструменти оцінки активів надійніші за електронні таблиці та навіщо фіксувати версії.

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

Що дає бібліотека для оцінки активів, чого немає у формулі в електронній таблиці

Комірка з формулою Black-Scholes в електронній таблиці потребує п'яти вхідних параметрів і повертає ціну. Формула — це найпростіша частина. Навколо неї існують чотири рівні, і саме вони складають бібліотеку.

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

ЗапитКрива дохідності Treasury на останню дату котирування
Точний 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-розклади вирішують системи, що виникають при підгонці, квадратури та інтегрування Фур'є оцінюють моделі, формули яких є інтегралами, а пошук коренів вираховує implied volatility. Цей рівень нудний, і саме його найчастіше реалізують неякісно. Класичний приклад — власноруч написаний розв'язувач Ньютона, який збігається на ліквідних 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 як до мінливої цілі, і дотримуйтеся кількох правил:

  • Фіксуйте точний тегований реліз у своєму lock-файлі та оновлюйтеся цілеспрямовано, використовуючи власний набір тестів як фільтр.
  • Тримайте тонку власну обгортку навколо типів бібліотеки, щоб перейменування потребувало змін в одному файлі, а не в сорока.
  • Записуйте версію бібліотеки поруч із числами, які вона згенерувала, щоб повторний запуск, який дає інший результат, вказував на оновлення, а не на ринок.
  • Зберігайте показники маржі, забезпечення та регуляторного ризику на тому, що гарантує сумісність, доки не вийде версія 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 для кожного контракту створює накладні витрати на кожному переході, і рішення полягає в тому, щоб передати рушію масив і отримати масив назад. Це ніша, на яку націлений itofin, поряд із QuantLib-Python.

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

Де насправді живе ціль калібрування

Калібрування потребує чогось, до чого можна підігнати модель, і цим «чимось» є поверхня ринкових implied volatilities. Сторона пошуку коренів цього процесу описана в як розраховується implied volatility. Форма нижче — це те, чому має відповідати модель.

ЗапитРівень implied volatility для near-the-money опціонів на 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

Біля ціни (near the money) контракти AAPL у кошику 0 to 7 days мали середню implied volatility 29.6% за сесії, що розглядаються, проти 29.3% за 241 days or more. Модель з одним параметром волатильності не може одночасно перебувати в обох точках, що є причиною існування моделей зі структурою термінів волатильності. Підгонка моделі до такої поверхні — це етап калібрування, а чутливості, що випливають із підігнаної моделі, — це греки, описані в поясненні опціонних греків.

Як були побудовані ці панелі

Панель кривих зчитує єдину найостаннішу котировану дату в серії казначейських облігацій і розгортає її одинадцять стовпців термінів погашення в рядки, відкидаючи будь-який термін без котирування того дня. Панель сесій рахує окремі дати на стрічці SPY за місяць, що є чистим проксі для підрахунку повних біржових сесій. Панель ланцюгів рахує окремі коди контрактів із ненульовим обсягом на останній доступній сесії, згруповані за базовим активом. Панель волатильності зберігає лише збіжні розв'язки з ненульовим обсягом і страйками в межах 5% від ціни закриття базового активу, що є стандартним діапазоном near-the-money; передній кошик включає експірації того ж дня.

FAQ

Чи є libitofin прямою заміною QuantLib?

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

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

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

Чи потрібно мені писати на Rust, щоб використовувати libitofin?

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

Що дає мені бібліотека для оцінки активів, чого немає у формулі в електронній таблиці?

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

Чи змінює мова програмування ціну опціону?

Математично — ні. Вона змінює відтворюваність: результати з плаваючою комою залежать від порядку операцій, тому дві реалізації однієї моделі можуть відрізнятися в останніх цифрах. Використання одного рушія для досліджень і продакшену усуває цей розрив.


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