Skip to main content

Python bindings for libitofin: a Rust port of QuantLib for pricing, risk, and calibration.

Project description

itofin

Idiomatic Python bindings over the libitofin Rust core, a ground-up port of QuantLib for pricing, risk, and calibration. The heavy numerics run in Rust; Python drives them. The pricing snippets below reproduce the cached Rust test oracles exactly; the abbreviated calibration snippets converge to the same targets within the tests' calibration tolerance (the full matrices live in tests/).

Install

pip install itofin

Requires Python >= 3.13.

Price a European option and its greeks

import itofin

s = itofin.Settings()
s.set_evaluation_date(itofin.Date(15, 6, 2026))
dc = itofin.DayCounter.actual360()

process = itofin.BlackScholesProcess(60.0, 0.08, 0.0, 0.30, itofin.Date(15, 6, 2026), dc)
option = itofin.VanillaOption(itofin.OptionType.Call, 65.0, itofin.Date(15, 6, 2026) + 90, s)
option.set_engine(process)

print(f"NPV   {option.npv():.10f}")
print(f"delta {option.delta():.10f}")
print(f"vega  {option.vega():.10f}")
print(f"rho   {option.rho():.10f}")
NPV   2.1333684449
delta 0.3724827980
vega  11.3515440535
rho   5.0538998582

Price under Heston and calibrate to a flat-vol surface

Semi-analytic Heston pricing:

import itofin

s = itofin.Settings()
s.set_evaluation_date(itofin.Date(27, 12, 2004))
dc = itofin.DayCounter.actual_actual_isda()

# HestonProcess(risk_free, dividend, spot, v0, kappa, theta, sigma, rho, reference_date, day_counter)
process = itofin.HestonProcess(
    0.0225, 0.02, 1.0, 0.1, 3.16, 0.09, 0.4, -0.2, itofin.Date(27, 12, 2004), dc
)
model = itofin.HestonModel(process)
option = itofin.VanillaOption(itofin.OptionType.Call, 1.05, itofin.Date(28, 3, 2005), s)
option.set_heston_engine(model, 64)

print(f"Heston NPV {option.npv():.10f}")
Heston NPV 0.0404774515

Calibrating the model to a flat 10% vol surface drives the vol-of-vol sigma toward zero and both v0 and theta toward the flat variance (0.10 squared = 0.01). The snippet below fits a 3-maturity by 3-strike grid; the full 21-helper matrix and its tighter tolerances live in tests/test_heston_calibration.py.

import math
import itofin

s = itofin.Settings()
s.set_evaluation_date(itofin.Date(15, 1, 2026))
dc = itofin.DayCounter.actual360()
ref = itofin.Date(15, 1, 2026)
risk_free = itofin.FlatForward(ref, 0.04, dc)
dividend = itofin.FlatForward(ref, 0.50, dc)
VOL = 0.10

helpers = []
for n in (1, 2, 3):
    tau = float(n)
    forward = dividend.discount(tau) / risk_free.discount(tau)  # spot = 1.0
    for moneyness in (-1.0, 0.0, 1.0):
        strike = forward * math.exp(-moneyness * VOL * math.sqrt(tau))
        helpers.append(itofin.HestonModelHelper(
            itofin.Period(n, "Years"),
            itofin.Calendar.null_calendar(),
            1.0,        # spot
            strike,
            VOL,        # 10% flat vol
            0.04,       # risk-free
            0.50,       # dividend yield
            itofin.CalibrationErrorType.RelativePriceError,
            ref,
            dc,
            s,
        ))

process = itofin.HestonProcess(0.04, 0.50, 1.0, 0.01, 0.2, 0.02, 0.5, -0.75, ref, dc)
model = itofin.HestonModel(process)
method = itofin.LevenbergMarquardt(1e-8, 1e-8, 1e-8, False)
end_criteria = itofin.EndCriteria(400, 40, 1e-8, 1e-8, 1e-8)
model.calibrate(helpers, method, end_criteria, 96)

print(f"sigma {model.sigma():.6f}")
print(f"v0    {model.v0():.6f}")
print(f"theta {model.theta():.6f}")
sigma 0.000216
v0    0.010000
theta 0.010000

Calibrate Hull-White to a swaption matrix

import itofin

s = itofin.Settings()
s.set_evaluation_date(itofin.Date(15, 2, 2002))
curve = itofin.FlatForward(itofin.Date(19, 2, 2002), 0.04875825, itofin.DayCounter.actual365_fixed())

model = itofin.HullWhite(curve, 0.1, 0.01)
index = itofin.Euribor.six_months(curve, s)

# (option tenor, swap tenor, Black vol); the full co-terminal matrix is in the tests.
swaptions = [(1, 5, 0.1148), (2, 4, 0.1108), (3, 3, 0.1070), (4, 2, 0.1021), (5, 1, 0.1000)]
helpers = [
    itofin.SwaptionHelper(
        itofin.Period(maturity, "Years"),
        itofin.Period(length, "Years"),
        vol,
        index,
        itofin.Period(1, "Years"),
        itofin.DayCounter.thirty360_bond_basis(),
        itofin.DayCounter.actual360(),
        curve,
        itofin.CalibrationErrorType.RelativePriceError,
        1.0,
    )
    for maturity, length, vol in swaptions
]

method = itofin.LevenbergMarquardt(1e-8, 1e-8, 1e-8, False)
end_criteria = itofin.EndCriteria(10000, 100, 1e-6, 1e-8, 1e-8)
model.calibrate(helpers, method, end_criteria, False)  # fix_reversion=False, fit a and sigma

print(f"a     {model.a():.7f}")
print(f"sigma {model.sigma():.7f}")
a     0.0464113
sigma 0.0057992

Price a European swaption with the Jamshidian engine

import itofin

s = itofin.Settings()
s.set_evaluation_date(itofin.Date(15, 1, 2026))
curve = itofin.FlatForward(itofin.Date(15, 1, 2026), 0.03, itofin.DayCounter.actual365_fixed())

fixed = itofin.Schedule(
    itofin.Date(15, 1, 2028), itofin.Date(15, 1, 2033),
    itofin.Frequency.Annual, itofin.Calendar.target(),
    itofin.BusinessDayConvention.Unadjusted,
)
floating = itofin.Schedule(
    itofin.Date(15, 1, 2028), itofin.Date(15, 1, 2033),
    itofin.Frequency.Semiannual, itofin.Calendar.target(),
    itofin.BusinessDayConvention.Unadjusted,
)
index = itofin.Euribor.six_months(curve, s)

swap = itofin.VanillaSwap(
    itofin.SwapType.Payer, 100.0,
    fixed, 0.03, itofin.DayCounter.thirty360_bond_basis(),
    floating, index, 0.0, itofin.DayCounter.actual360(), s,
)
swaption = itofin.Swaption(
    swap, itofin.EuropeanExercise(itofin.Date(15, 1, 2027)),
    itofin.SettlementType.Physical, itofin.SettlementMethod.PhysicalOTC, s,
)
swaption.set_jamshidian_engine(itofin.HullWhite(curve, 0.05, 0.01))

print(f"payer swaption NPV {swaption.npv():.10f}")
payer swaption NPV 1.5666103956

License

BSD-3-Clause, matching the libitofin core. See the repository for the layer status table, the divergences-from-QuantLib catalogue, and the Rust test oracles behind the numbers above.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

itofin-0.6.1.tar.gz (2.1 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

itofin-0.6.1-cp313-abi3-win_amd64.whl (466.9 kB view details)

Uploaded CPython 3.13+Windows x86-64

itofin-0.6.1-cp313-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (669.2 kB view details)

Uploaded CPython 3.13+manylinux: glibc 2.17+ x86-64

itofin-0.6.1-cp313-abi3-macosx_11_0_arm64.whl (590.0 kB view details)

Uploaded CPython 3.13+macOS 11.0+ ARM64

File details

Details for the file itofin-0.6.1.tar.gz.

File metadata

  • Download URL: itofin-0.6.1.tar.gz
  • Upload date:
  • Size: 2.1 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for itofin-0.6.1.tar.gz
Algorithm Hash digest
SHA256 de6876254fe231628059dba3b6e78674ae1fcbaee338baf81b874c9e214a2cff
MD5 1679a98e1fb963e15c76c79fea98079f
BLAKE2b-256 2e3f6b39f713caf9309834c95eec8a1f2f924d8b87af597bbf920b7f5c79812e

See more details on using hashes here.

Provenance

The following attestation bundles were made for itofin-0.6.1.tar.gz:

Publisher: semantic-release.yml on benbenbang/libitofin

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file itofin-0.6.1-cp313-abi3-win_amd64.whl.

File metadata

  • Download URL: itofin-0.6.1-cp313-abi3-win_amd64.whl
  • Upload date:
  • Size: 466.9 kB
  • Tags: CPython 3.13+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for itofin-0.6.1-cp313-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 bd9694c5c3d98614eebe40904542c9d249e75c42b72ac739edac1e57b224febd
MD5 977a5553e51eafe72c2ef0303d2d65af
BLAKE2b-256 16083f53b9abe0e89b4dd2903b60763d9b6eb2cb285dd4a8a128e169b22671ed

See more details on using hashes here.

Provenance

The following attestation bundles were made for itofin-0.6.1-cp313-abi3-win_amd64.whl:

Publisher: semantic-release.yml on benbenbang/libitofin

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file itofin-0.6.1-cp313-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for itofin-0.6.1-cp313-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 f104ed4c4a4ee7f0c84b85feac44b30412865c1e0d44b173cca23c92d3793545
MD5 f4b7888eb08be90fd1aa0f575e5b6cb7
BLAKE2b-256 17a0d6e986126ef8f5c31dae17ab05043c436e66384981e1d767cb714e8173e6

See more details on using hashes here.

Provenance

The following attestation bundles were made for itofin-0.6.1-cp313-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: semantic-release.yml on benbenbang/libitofin

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file itofin-0.6.1-cp313-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for itofin-0.6.1-cp313-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 4564362cc73d64520c944b71582b1892fc5e9810283e81f830c6ecc2086f2c28
MD5 66b36e0cf473ab153281833266517df3
BLAKE2b-256 d99822b064046a1373aec409e1c0c9b6f818d84bcd8df81177a2e6fb48f33e98

See more details on using hashes here.

Provenance

The following attestation bundles were made for itofin-0.6.1-cp313-abi3-macosx_11_0_arm64.whl:

Publisher: semantic-release.yml on benbenbang/libitofin

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page