{"id":51632658,"url":"https://github.com/benbenbang/libitofin","last_synced_at":"2026-07-13T09:33:22.554Z","repository":{"id":367296042,"uuid":"1280164540","full_name":"benbenbang/libitofin","owner":"benbenbang","description":"High-performance quantitative finance library in Rust: pricing, risk, and numerical tools built for speed and safety.","archived":false,"fork":false,"pushed_at":"2026-07-11T09:07:03.000Z","size":2310,"stargazers_count":1,"open_issues_count":17,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-11T10:05:28.182Z","etag":null,"topics":["finance","quant","quantitative-finance","quantlib","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/benbenbang.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-25T10:26:15.000Z","updated_at":"2026-07-11T09:07:09.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/benbenbang/libitofin","commit_stats":null,"previous_names":["benbenbang/libitofin"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/benbenbang/libitofin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benbenbang%2Flibitofin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benbenbang%2Flibitofin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benbenbang%2Flibitofin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benbenbang%2Flibitofin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/benbenbang","download_url":"https://codeload.github.com/benbenbang/libitofin/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benbenbang%2Flibitofin/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35418222,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-13T02:00:06.543Z","response_time":119,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["finance","quant","quantitative-finance","quantlib","rust"],"created_at":"2026-07-13T09:33:21.930Z","updated_at":"2026-07-13T09:33:22.548Z","avatar_url":"https://github.com/benbenbang.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Lib-Itô-Fin\n\n[![Crates.io](https://img.shields.io/crates/v/libitofin)](https://crates.io/crates/libitofin)\n[![docs.rs](https://img.shields.io/docsrs/libitofin)](https://docs.rs/libitofin)\n[![License: BSD-3-Clause](https://img.shields.io/crates/l/libitofin)](LICENSE)\n\nA ground-up port of [QuantLib](https://github.com/lballabio/QuantLib) — the\nquantitative-finance library — into idiomatic, memory-safe Rust. The deliverable\nis a core library, **`libitofin`**, with thin language bindings on top (Python\nfirst, then a C ABI for everything else).\n\n\u003e The name nods to [Kiyosi Itô](https://en.wikipedia.org/wiki/Kiyosi_It%C5%8D),\n\u003e whose stochastic calculus underpins modern derivatives pricing.\n\n\u003e ⚠️ **Pre-1.0, under active development.** The core prices a European option\n\u003e end-to-end today, but the API will change until 1.0 and large parts of the\n\u003e pricing surface (processes, cashflows, most instruments and engines) are still\n\u003e being filled in. See [Status](#status).\n\n```sh\ncargo add libitofin\n```\n\n```rust\nuse libitofin::time::Date;\n// dates, calendars, day counters, curves, vol surfaces, quotes, and an\n// analytic European-option engine are available today - see docs.rs.\n```\n\n## Why\n\nQuantLib is ~470k lines of mature, battle-tested C++ across 16 modules. This\nproject re-expresses that core in safe, idiomatic Rust:\n\n- **Memory-safe by construction** — no manual `shared_ptr` cycles or\n  use-after-free. The core is single-threaded-mutable during setup, then frozen\n  into immutable snapshots for data-race-free parallel compute (`rayon`).\n- **A clean FFI story** — a single core crate with Python (PyO3) and C-ABI\n  (cbindgen) bindings layered on top, so the same engine is reachable from\n  Python, C, C++, Julia, R, and more.\n- **Faithful numerics** — QuantLib's `test-suite/` (186 `.cpp` files) is the\n  porting oracle: a feature is \"done\" only when the matching tests are ported and\n  the Rust output matches the C++ numbers within tolerance.\n- **Usability at the edges** — where C++ leans on runtime casts, silent\n  fallbacks, or clock magic, the core prefers compile-time typing and explicit\n  errors; ergonomic conveniences live in the binding crates.\n\n## Status\n\nThe port proceeds **bottom-up** through dependency layers L0→L11; each layer\ndepends only on lower-numbered layers. The live backlog is the\n[GitHub Project board](https://github.com/users/benbenbang/projects/5) and the\nrepository's issues (the board is the source of truth, not a checked-in file).\n\n| Layer | Epic | Scope | State |\n|------|------|-------|-------|\n| **L0** | core | types, errors, patterns, settings, handle, utilities | ✅ done |\n| **L1** | math | array/matrix, distributions, interpolation, integrals, solvers, optimization, statistics, RNG, ODE, copulas, decompositions | ✅ done |\n| **L2** | time | `Date`, `Period`, `Calendar`, `DayCounter`, `Schedule`, IMM/ASX/ECB | ✅ done |\n| **L3** | quotes | `Quote`, `SimpleQuote`, derived quotes, `InterestRate`, compounding | ✅ done |\n| **L4** | term structures | interpolated yield curves, Black-vol curves/surfaces, local vol | ✅ done |\n| L5 | processes | `StochasticProcess`, Black-Scholes, Heston, … | 🚧 in progress |\n| L6–L11 | indexes, cashflows, instruments, methods, models, engines | ⬜ planned |\n\n**Milestone 1 (done):** a European option prices end-to-end — quote → flat\nyield/vol curves → generalized Black-Scholes process → analytic engine → lazy\ninstrument greeks — matching QuantLib's `europeanoption.cpp` value and greeks to\ndouble-rounding precision, with the full observer/invalidation graph exercised.\n\n### What's usable today\n\n- **`types` / `errors`** — QuantLib's numeric aliases and `QlError` / `QlResult`\n  with `fail!` / `require!` macros (the analogue of `QL_FAIL` / `QL_REQUIRE`).\n- **`patterns` / `handle` / `settings`** — the observer/observable graph,\n  `LazyObject`, `Handle` / `RelinkableHandle`, and the evaluation-date context.\n- **`math`** — arrays and matrices (with SVD/QR/Cholesky/…), the distribution\n  family, interpolation (linear → bicubic), integrals (incl. Gauss quadratures),\n  1-D solvers, optimizers, statistics, RNGs (MT/Sobol/…), ODEs, copulas.\n- **`time`** — dates, periods, 50+ calendars, day counters, schedules, IMM/ASX/ECB.\n- **`quotes` / `interestrate`** — simple and derived quotes, interest-rate and\n  compounding conversions.\n- **`termstructures`** — flat and interpolated yield curves (zero/discount/\n  forward), implied and spreaded curves, Black-variance curves/surfaces, local vol.\n- **`processes` / `instruments` / `pricingengines`** — the generalized\n  Black-Scholes process, vanilla payoffs and exercise, `EuropeanOption`, and the\n  analytic European engine.\n\n## Getting started (development)\n\nRequires the toolchain pinned in [`rust-toolchain.toml`](rust-toolchain.toml)\n(Rust 1.96.0, edition 2024); plain `cargo` picks it up automatically.\n\n```sh\ncargo build                          # whole workspace\ncargo build -p libitofin             # core crate only\n\ncargo test                           # the porting oracle\ncargo test -p libitofin patterns::   # one module\n\ncargo fmt\ncargo clippy --all-targets\n```\n\nA [`pre-commit`](https://pre-commit.com/) config runs `fmt`, `check`, `clippy`,\n`test`, and conventional-commit linting on every commit:\n\n```sh\npre-commit run --all-files\n```\n\n## Project layout\n\n```\ncrates/libitofin/       the core library — FFI-agnostic, idiomatic Rust\ncrates/libitofin-ffi/   extern \"C\" + cbindgen → C header          (planned)\ncrates/libitofin-py/    PyO3 + maturin → pip-installable wheel     (planned)\nQuantLib/               reference C++ tree + test oracle           (git-ignored symlink)\n```\n\nThe `QuantLib/` entry is a **git-ignored local symlink**, not committed — point\nit at a QuantLib checkout to have the reference source and test-suite oracle\navailable locally: `ln -s /path/to/QuantLib QuantLib`.\n\n## Design principles\n\n- **Bottom-up, layer by layer** — never port a module before its dependencies.\n- **The C++ test-suite is the oracle** — match the numbers, not just the shape.\n- **Small PRs** — ≤350 LOC target, 400 hard cap; large source files split across\n  tickets.\n- **Single-threaded-mutable core, snapshot-and-fan-out for parallelism** — the\n  observable graph is mutated single-threaded during setup, then frozen into\n  immutable snapshots for `rayon` compute. No `async` in the core (QuantLib does\n  no I/O; market data is user input).\n- **Fidelity in numerics, usability at API boundaries** — QuantLib is the oracle\n  for every number, but the core favours compile-time typing and explicit `Result`\n  errors over runtime casts and silent fallbacks; convenience lives in the bindings.\n\n## Divergences from QuantLib\n\nThe port targets **semantic** faithfulness (matching QuantLib's results), not\nbit-for-bit reproduction of its implementation. A small, deliberate set of\ndivergences is catalogued here. Each is an intentional, reviewed decision (not an\noversight) and is documented at the point of divergence in the source.\n\n**Time / calendars (EPIC-2):**\n\n- **Calendar holiday overrides are per-value, not process-global.** QuantLib\n  shares one global `Impl` per market, so `addHoliday` on any `TARGET()` handle\n  is visible through every other. This port shares added/removed holidays only\n  among *clones* of a `Calendar` value, matching the \"explicit state, no hidden\n  singletons\" decision. The built-in holiday rules are identical; only the\n  reach of `add_holiday`/`remove_holiday` differs.\n- **`holiday_list` filters weekends by a date-aware rule.** QuantLib's\n  `holidayList` excludes weekends using the weekday-only `isWeekend`, which\n  misclassifies holidays for markets whose weekend changed over time (Saudi\n  Arabia's Thu/Fri→Fri/Sat in 2013, Israel/TASE's Fri/Sat→Sat/Sun in 2026). This\n  port filters with a date-aware `is_weekend_on`; fixed-weekend calendars are\n  unaffected (the default equals the weekday rule).\n- **Table-backed calendars fail loudly past their data horizon.** Where QuantLib\n  tabulates lunar / religious / observed holidays only up to a fixed year and\n  then silently returns \"business day\" for later dates, this port panics with a\n  clear message once a query passes the last fully-tabulated year. QuantLib's\n  tables are kept verbatim; we never fabricate future dates.\n- **`Period` comparison is a partial order, and fixes a negative-period bug.**\n  QuantLib's `operator\u003c`/`operator==` throw when two periods have overlapping\n  day ranges (e.g. `1 Month` vs `30 Days`); this port returns `None` from\n  `partial_cmp` instead, so comparison never panics. It also orders the day\n  bounds `min \u003c= max` before comparing, which QuantLib omits: for negative\n  lengths QuantLib's inverted bounds make overlapping periods (like `-1 Month`\n  vs `-30 Days`) look decidably ordered, whereas this port correctly reports\n  them as undecidable. Positive comparisons are unaffected.\n- **`DayCounter` is always valid; there is no empty placeholder.** QuantLib's\n  default-constructed `DayCounter` holds a null `impl_` and `QL_REQUIRE`s a\n  non-null one on every call. This port omits the empty state, so a\n  `DayCounter` always wraps a concrete convention and its accessors never trip\n  that null check. The \"not yet set\" placeholder used by higher layers is an\n  `Option\u003cDayCounter\u003e` at those call sites instead.\n- **`Business/252` counts directly instead of via a process-global cache.**\n  QuantLib memoizes per-month and per-year business-day totals in global\n  `std::map`s keyed by calendar name; this port counts with\n  `Calendar::business_days_between` directly. With a calendar's built-in\n  schedule the results are identical; once holidays are overridden they can\n  differ, since QuantLib's name-keyed cache goes stale while this port always\n  reflects the current holiday set.\n- **`Actual/Actual (ISMA)` uses the reference-date algorithm.** QuantLib picks a\n  schedule-driven implementation when a `Schedule` is supplied and a\n  reference-date one otherwise; the reference-date path is ported, with the\n  schedule-driven overload following as needed.\n\n**Core (EPIC-0):**\n\n- **An unset evaluation date is an explicit error, not a system-clock fallback.**\n  QuantLib's `Settings` singleton falls back to the machine clock; this core has\n  no clock (for determinism and FFI), so operations that need the evaluation date\n  return `Err` when it is unset rather than silently pricing a possibly-expired\n  instrument as live.\n- **Past index fixings live on `Settings`, not in a global `IndexManager`\n  singleton (D11).** QuantLib stores fixing history in `IndexManager::instance()`,\n  a global-singleton `map\u003cstring, TimeSeries\u003cReal\u003e\u003e`; D5 forbids that singleton,\n  so the history moves onto `Settings` as a `fixing_store`, exactly like the\n  evaluation date. It stays shared across every handle to an index (a `Settings`\n  is shared, and the store is keyed by the case-insensitive index name), so two\n  handles to \"the same\" Euribor observe one history, as C++'s global map\n  guarantees. Each index name owns an `Observable`, so adding or clearing a\n  fixing notifies exactly that index's observers, and those notifiers outlive a\n  `clear_fixings`. A conflicting fixing on an existing date returns `Err` (D4)\n  where QuantLib notifies and then throws. The `enforces_todays_historic_fixings`\n  flag's today's-fixing rule is applied by the index layer that reads the store,\n  as `InterestRateIndex::fixing` does in C++, not by the store itself.\n\n**Cash flows (EPIC-7):**\n\n- **`Coupon` is not a subtrait of `CashFlow`; one blanket impl derives the\n  cash-flow face from the coupon.**\n  C++ derives `Coupon` from `CashFlow`, and its base class supplies\n  `hasOccurred`, `exCouponDate()` and `isCoupon()` so that no coupon can forget\n  them. Rust has no specialization, so under the subtrait shape a `Coupon`\n  cannot supply its supertrait's methods on its implementors' behalf. All three\n  wrong answers compile and are plausible numbers rather than diagnostics: the\n  plain-event `has_occurred` ignores `Settings::includeTodaysCashFlows` on the\n  evaluation date; a `None` `ex_coupon_date` accrues ex-coupon while reporting\n  `trading_ex_coupon` as `false`; a `None` `as_coupon` contributes nothing to\n  `bps`, has its amount subtracted from `atm_rate`'s target, and reports its\n  payment date to `maturity_date` in place of its accrual end.\n\n  So `Coupon` restates its own surface (`coupon_base`, `amount`, `rate`,\n  `day_counter`, `accrued_amount`, and `AsObservable`), and\n  `impl\u003cT: Coupon\u003e CashFlow for T` writes the three exactly once, reading\n  `CouponBase`. A coupon cannot get them wrong because it cannot supply them.\n  `amount` moves onto `Coupon` for the same reason it must: a blanket that\n  *provided* `amount` would give `FixedRateCoupon` a generic body in place of\n  its compounded one, and a competing `impl CashFlow for FixedRateCoupon`\n  restoring it is a conflicting implementation. The three methods stay required\n  on `CashFlow` and `Event` for the flows that are not coupons, which still\n  forward explicitly to `event_has_occurred` or `cash_flow_has_occurred`.\n\n  Two costs. `dyn Coupon` does not implement `CashFlow`: the `Some(self)` of\n  `as_coupon` is an unsizing coercion, so the blanket takes a `Sized` `T`, and\n  `+ ?Sized` would restore the impl and forbid the `Some(self)`. Nothing needs\n  it - the `CashFlows` analytics hold a `\u0026dyn CashFlow` already and read only\n  `Coupon`'s own methods through the coupon view. And `amount` now sits on both\n  traits, so on a concrete coupon with both in scope an unqualified\n  `coupon.amount()` is ambiguous; name the trait.\n\n- **The `IborCoupon` par/indexed forecast switch is threaded on `Settings`, not\n  a global singleton.** QuantLib's `IborCoupon::indexFixing()` forecast branch\n  selects between a par-coupon approximation (rolls `fixingEndDate` off the\n  coupon's own accrual end) and an indexed coupon (the index's natural maturity)\n  through the `IborCoupon::Settings` singleton (`usingAtParCoupons`, default\n  `true`). That process-global switch is what D5 forbids, so the port drops the\n  singleton and threads the flag explicitly on `Settings`\n  (`using_at_par_coupons`, default `true`) beside the evaluation date and the\n  D11 fixing store. The behaviour it governs is fully reproduced: `index_fixing`\n  computes the cached `fixingValueDate`/`fixingEndDate`/`spanningTime`\n  (`couponpricer.cpp:56-88`) and reads the specialized 3-arg `forecastFixing`\n  off the curve; a *determined* fixing is mode-independent and returns the same\n  required past fixing as the C++ override. `BlackIborCouponPricer` ports\n  `swapletRate = gearing * indexFixing + spread`;\n  since a non-in-arrears `Black76` coupon needs no convexity adjustment it needs\n  no volatility, and since only `swapletPrice` (not `swapletRate`) reads the\n  forwarding-curve discount, the pricer captures no curve. The caplet/floorlet\n  optionlet path, the `*Price` methods, and the in-arrears convexity adjustment\n  belong to the cap/floor slice; an in-arrears coupon is refused with an `Err`\n  rather than priced with a missing convexity term.\n\n- **`IborLeg` builds the plain-vanilla coupons only; caps, floors and the\n  in-arrears feature are omitted, not silently dropped.** QuantLib's `IborLeg`\n  carries `withCaps`/`withFloors`/`inArrears` builder methods that route the leg\n  through `CappedFlooredIborCoupon` and the optionlet pricer. Those belong to the\n  cap/floor slice, so this port omits the methods entirely rather than accepting\n  the vectors and building plain coupons. The default `BlackIborCouponPricer` is\n  attached in `coupons()` (C++ attaches it in `operator Leg()` under the same\n  no-cap/no-floor/no-arrears condition, which here always holds); `build()` only\n  erases the concrete coupons into a `Leg`, and the free `set_coupon_pricer`\n  overrides the default on the concrete coupons, the erased `Leg` carrying no\n  downcast. The stub reference dates follow the `FloatingLeg` template\n  (`calendar.adjust(end - tenor, bdc)`, no end-of-month flag), which agrees with\n  the fixed leg on every multi-coupon schedule. A zero gearing, which the C++\n  template collapses to a `FixedRateCoupon`, is not special-cased: `IborCoupon`\n  rejects it, so `with_gearing(0.0)` surfaces that `Err` rather than a silent\n  fixed coupon.\n\n**Indexes (EPIC-6):**\n\n- **`Currency` is always valid; there is no empty placeholder.** QuantLib's\n  default-constructed `Currency` holds a null `data_` and `QL_REQUIRE`s a\n  non-null one on every inspector; its `operator==` treats two empty currencies\n  as equal and `operator\u003c\u003c` prints `\"null currency\"`. This port omits the empty\n  state, so a `Currency` always holds a concrete specification: accessors never\n  trip that null check, equality is purely by name (QuantLib's non-empty\n  branch), and `Display` always prints the ISO code. The \"not yet set\"\n  placeholder is an `Option\u003cCurrency\u003e` at higher call sites, mirroring the\n  `DayCounter` decision above. The `rounding` convention, `triangulationCurrency`\n  and `minorUnitCodes` fields are dropped as unused by the index slice; rounding\n  returns with the money layer. Only EUR is provided; the `ql/currencies/*`\n  catalogue is deferred.\n\n**Pricing engines (Milestone 1):**\n\n- **Zero-volatility Black greeks dispatch on the stored option type, not on the\n  sign of `alpha_`.** This is a deliberate divergence where the oracle is\n  wrong, and the only place in the port where a *finite* priced number\n  intentionally disagrees with QuantLib. Upstream (tree `v1.42.1-266-g9863b578a`,\n  from commit `17f1a1bed` \"Fixing zero vol for Black\") detects the option type\n  in its zero-vol branches with `if (alpha_ \u003e= 0) // Call`, at nine sites\n  (`blackcalculator.cpp:215,222,229,257,264,271,439,446,453`). For a\n  plain-vanilla put, `alpha_ = -1.0 + cum_d1_` (`blackcalculator.cpp:137`), and\n  at zero volatility an out-of-the-money put has `cum_d1_ == 1.0` exactly, so\n  `alpha_ == 0.0` exactly and `0.0 \u003e= 0` takes the Call branch: the OTM put is\n  handed a delta of `+1.0` where the correct value is `0.0`. This port instead\n  dispatches on the option type stored in the calculator, implementing the\n  values the reference's own comments state, so the OTM-put delta is `0.0`. The\n  full zero-vol ladder is pinned by `zero_volatility_ladder_matches_stated_intent`\n  and `zero_volatility_otm_put_gets_put_greeks_not_call_greeks` in\n  `blackcalculator.rs`, which assert the corrected numbers (OTM `0.0`, ATM\n  `-0.5`, ITM `-1.0` for the put, and the call mirror), so a regression to the\n  `alpha_ \u003e= 0` form is a test failure.\n\n**Non-finite inputs (cross-cutting):**\n\n- **Non-finite arguments are rejected at the API boundary.** QuantLib validates\n  signs (`stdDev \u003e= 0`, `forward \u003e 0`, `t \u003e= 0`) and relies on NaN failing every\n  such comparison, so a NaN is already an error wherever a sign is checked. An\n  infinity is not: it passes `\u003e= 0.0` and propagates to a NaN result several\n  layers down, and where a curve extrapolates the range check is skipped\n  entirely. Following D10, this port widens each of those guards from \"not NaN\"\n  to \"finite\", and adds finiteness checks where C++ has none at all: solver\n  arguments and functor values (`solver1d.rs`), sampled quadrature abscissae\n  (`discrete.rs`), Black-Scholes process arguments (`blackscholesprocess.rs`),\n  and the volatility and variance an implementation returns\n  (`termstructures/volatility/`). Each site names the C++ guard it extends, or\n  states that none exists. The only behavioural change is for infinities; every\n  finite input QuantLib accepts is still accepted, so no priced number moves.\n- **Statistics accumulators reject a NaN sample value, and accept infinities.**\n  QuantLib's only sample guard is `QL_REQUIRE(weight \u003e= 0.0)`\n  (`generalstatistics.hpp:233`, `incrementalstatistics.cpp:127`), which this\n  port keeps verbatim, written `!(weight \u003e= 0.0)` so a NaN weight fails it as it\n  does in C++. A NaN *value* has no C++ guard: it is accumulated and poisons\n  every subsequent mean, variance and percentile with no diagnostic. Infinite\n  values remain accepted, as in C++, being meaningful to `min`, `max` and the\n  risk measures.\n- **Shape mismatches panic with a named cause.** `SVD::solveFor`\n  (`svd.cpp:528`) and the default `CostFunction::gradient` / `jacobian` have no\n  `QL_REQUIRE`; a wrongly-sized output leaves stale entries the optimiser reads\n  as real derivatives. These are caller errors, not market-data errors, so the\n  port asserts rather than returning `Err`.\n\n## License\n\n[BSD-3-Clause](LICENSE) — the same license as QuantLib, the ported source.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenbenbang%2Flibitofin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenbenbang%2Flibitofin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenbenbang%2Flibitofin/lists"}