Rust版QuantLib「libitofin」の概要と導入のポイント
RustでQuantLibを再構築するlibitofinとPython用itofinを紹介します。スプレッドシートと比較した価格評価ライブラリの利点や、バージョン一・〇未満のプロジェクトでタグ付きリリースを固定すべき理由を解説します。
libitofinは、Rust版QuantLibと呼べるライブラリです。2000年代初頭からデリバティブ価格評価のオープンソースにおける標準となっているC++ライブラリ「QuantLib」をRustへ移植したもので、Pythonパッケージ「itofin」も提供されています。2026年8月時点で、本プロジェクトはバージョン1.0未満(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 iAug 10, 2026時点で、提示された曲線には7の期間が含まれており、3.79%の1 monthから5.25%の30 yearsまでとなっています。スプレッドシートでは、ルックアップ関数と固定された4%の数値で対応しますが、ライブラリでは曲線オブジェクトとして扱います。すべての金融商品は、このオブジェクトを基準に価格評価され、所定の補間法(ゼロ金利の線形補間、割引因子の対数線形補間、単調スプラインなど)および最終点以降の補外ポリシーが適用されます。このオブジェクトを1ベーシスポイント動かせば、ブック内のすべての感応度が整合的に変化します。
日割り計算(Day-count conventions)。 利息は年間の端数期間に対して発生し、その定義は金融商品ごとの慣行に従います。Actual/360は経過日数を360で割り、Actual/365は365で割ります。30/360方式は各月を30日とみなします。Business/252は、252日を1年として取引セッション数をカウントするため、休日が登録された実際の取引所カレンダーが必要です。例えば、100万ドルを年率5%で90日間借り入れた場合、Actual/360では1万2,500ドルの利息が発生し、Actual/365では1万2,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対象の12ヶ月間において、2026-07は22セッションを記録し、暦日では31日でした。この最初の数字は算術ルールからは導き出せません。休日カレンダーから算出されるものであり、月ごとに結果が異なります。ライブラリは市場や国ごとのカレンダーを提供しますが、スプレッドシートではユーザー自身がこれらを管理しなければなりません。
キャリブレーション(調整)機能。 モデルのパラメータは市場で直接提示されていません。市場価格の全体像にモデル価格を適合させ、市場の変動に合わせて再適合させることでパラメータを決定します。これは制約付き最小二乗問題であり、ライブラリはLevenberg-Marquardt最適化アルゴリズム、負の値を許容しないパラメータ変換、コスト関数、そして収束判定といった一連のループ処理を提供します。
数値計算層。 すべての基盤には線形代数と積分があります。QR分解やSVD分解は適合計算で生じる連立方程式を解き、求積法やフーリエ積分は積分形式のモデルを評価し、求根アルゴリズムはインプライド・ボラティリティを算出します。この層は地味ですが、実装の良し悪しが最も分かれる部分です。よくある失敗例は、流動性の高いアット・ザ・マネー(ATM)では収束するものの、アウト・オブ・ザ・マネー(OTM)では発散する自作のニュートン法です。また、特異に近い行列の逆行列計算で精度を失い、一見もっともらしいが誤ったパラメータを返すケースも少なくありません。ゼロから構築する場合は、ライブラリのAPIリファレンスよりもオープンソースのクオンツ取引書籍を参考にすることをお勧めします。
libitofinとは何か、そしてRust版QuantLibと言えるのか
結論から言えば、重要な意味において「イエス」です。QuantLibの設計をRustに移植したものであり、QuantLib自身のテストスイートを用いて検証されています。これは数値計算ライブラリの移植として誠実な手法です。結果を「あるべき姿」という主観的な期待値ではなく、参照実装と比較してチェックしているためです。Python向けにはitofinというパッケージが用意されており、C++のツールチェーンを介さずにRustエンジンを呼び出せます。
より重要なのは「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 DESCAug 11, 2026において、SPYでは5336の異なる契約が単一セッションで取引され、360に対してKOでした。広範なチェーンを一度評価するのは容易です。しかし、すべての銘柄について契約ごとに五つの感応度を計算し、引用更新のたびにブック全体を評価するとなれば、制約の異なる別のプログラムが必要になります。
ループが1分間に数千回の評価で済み、周辺作業がリサーチや分析、日次評価であるならば、既存のPythonライブラリを使い続けるべきです。QuantLibのPythonバインディングは成熟しており、同じC++エンジンを使い、最も幅広い金融商品をカバーし、長年の運用実績があります。リサーチコードにおいて速度がボトルネックになることは稀であり、カバレッジと正確性こそが重要です。
ループが頻繁に発生し、周辺コードがそうでない場合は、Pythonからコンパイル済みのエンジンを呼び出します。注意すべきは境界のコストです。Pythonから契約ごとに呼び出すとオーバーヘッドが発生するため、配列をまとめてエンジンに渡し、配列で受け取る設計が有効です。itofinはQuantLib-Pythonと並び、この領域をターゲットにしています。
価格評価ループそのものが製品である場合(クォーティングサービス内のプライサー、スケジュール厳守のリスク計算、Pythonがインストールされていない環境へのバイナリ配布など)は、Rustで記述します。二つ目の問いもここに帰着します。浮動小数点の結果は演算順序に依存するため、同じモデルを二度実装しても末尾の桁で不一致が生じることがあります。リサーチ用ノートブックと本番環境のサービスで結果が異なれば、原因究明に一週間を要することになります。両側から同じエンジンを使用すれば、この種の不一致を根本から排除できます。これこそが、どの言語で書かれていようとも、コンパイル済みのコアをバインディング経由で使うべき強力な論拠です。この考え方は戦略構築にも適用され、再現性のあるバックテストがその重要性を示しています。
キャリブレーションの対象はどこにあるのか
キャリブレーションには適合対象が必要であり、それは市場が示唆するボラティリティの曲面です。その求根計算についてはインプライド・ボラティリティの算出方法で解説しています。以下の形状がモデルの適合先です。
各数値の背後にある正確な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)アット・ザ・マネー付近では、0 to 7 daysバケットのAAPL契約は、対象期間中のセッション平均で29.6%のインプライド・ボラティリティを示し、29.3%に対して241 days or moreでした。単一のボラティリティパラメータを持つモデルでは両方の点に同時に適合できないため、ボラティリティの期間構造を持つモデルが必要となります。このような曲面に適合させる作業がキャリブレーションであり、適合モデルから導き出される感応度がギリシャ指標(Greeks)です。詳細はオプションのギリシャ指標の解説を参照してください。
これらのパネルの作成方法
曲線パネルは、国債シリーズの最新の提示日を読み込み、11の期間列を各行に展開し、その日に提示のなかった期間を除外しています。セッションパネルは、SPYのテープ上の月ごとの異なる日付をカウントしており、これは取引所全体のセッション数を表す適切な指標です。チェーンパネルは、利用可能な最新セッションにおいて、原資産ごとにグループ化された、ゼロ以外の出来高を持つ異なる契約コードをカウントしています。ボラティリティパネルは、収束した計算結果のうち、出来高がゼロではなく、かつ原資産終値の5%以内の権利行使価格を持つもののみを保持しています。これは標準的なアット・ザ・マネーの範囲であり、フロントバケットには当日満期の銘柄も含まれます。
FAQ
libitofinはQuantLibの完全な代替品ですか?
いいえ。2026年8月現在、QuantLibの一部をカバーし、QuantLib自身のテストスイートで結果を検証しているpre-1.0の移植版です。本番環境での利用には、Pythonバインディングを通じて利用するQuantLibの方が、より広範かつ安定した選択肢です。
価格評価ライブラリにおけるpre-1.0とはどういう意味ですか?
Rustのバージョン管理慣行において、0.xリリースは互換性を保証しません。次のマイナーバージョンで名称変更や削除が行われる可能性があります。実務上は、特定のタグ付きリリースを固定し、アップグレードのたびに自身のテストスイートを再実行することを意味します。
libitofinを使うためにRustを書く必要がありますか?
いいえ。itofinというPythonパッケージが公開されているため、通常のPythonプロセスからエンジンを呼び出せます。Rustを書く必要があるのは、価格評価ループそのものを製品として提供する場合です。
価格評価ライブラリはスプレッドシートの数式と何が違いますか?
単一の金利ではなく曲線を用いる点、実際の取引所カレンダーに基づいた日割り計算慣行、市場価格にモデルパラメータを適合させるキャリブレーションループ、そしてこれらすべてを支える検証済みの数値計算層を備えている点です。閉形式の数式は、業務のほんの一部に過ぎません。
プログラミング言語によってオプション価格は変わりますか?
数学的には変わりません。変わるのは再現性です。浮動小数点の結果は演算順序に依存するため、同一モデルの二つの実装で末尾の桁が不一致になることがあります。リサーチと本番環境で同じエンジンを使用すれば、この不一致を解消できます。
ここにあるすべてのパネルには、その基となるSQLが格納されています。展開してカウント方法を確認してください。同様の曲線、カレンダー、チェーンに関する質問は、Strasmoreターミナルで平易な英語で問い合わせ可能です。