{"id":50114939,"url":"https://github.com/nktkt/pricer","last_synced_at":"2026-05-23T14:01:11.637Z","repository":{"id":359577281,"uuid":"1246692946","full_name":"nktkt/pricer","owner":"nktkt","description":"pricer — a scalable pricing \u0026 risk engine in C++ with an LLVM JIT payoff compiler","archived":false,"fork":false,"pushed_at":"2026-05-22T15:08:10.000Z","size":367,"stargazers_count":0,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-22T18:50:25.244Z","etag":null,"topics":["cpp","jit","llvm","monte-carlo","option-pricing","quant","quantitative-finance"],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nktkt.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"ROADMAP.md","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-05-22T13:02:52.000Z","updated_at":"2026-05-22T15:08:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/nktkt/pricer","commit_stats":null,"previous_names":["nktkt/pricer"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/nktkt/pricer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nktkt%2Fpricer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nktkt%2Fpricer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nktkt%2Fpricer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nktkt%2Fpricer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nktkt","download_url":"https://codeload.github.com/nktkt/pricer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nktkt%2Fpricer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33398391,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-23T04:15:53.637Z","status":"ssl_error","status_checked_at":"2026-05-23T04:15:53.242Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["cpp","jit","llvm","monte-carlo","option-pricing","quant","quantitative-finance"],"created_at":"2026-05-23T14:01:05.435Z","updated_at":"2026-05-23T14:01:11.627Z","avatar_url":"https://github.com/nktkt.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pricer\n\nA header-only C++17 quant library: Black–Scholes, Monte Carlo, American /\nBermudan (early-exercise), path-dependent exotics (Asian / barrier / lookback / digital)\nand multi-asset baskets / spreads / rainbows pricing, a Bachelier (normal) model\nfor negative rates, model calibration (SVI, SABR, Heston, Dupire) and exact Greeks by\nautomatic differentiation, counterparty risk (xVA), and a **runtime payoff\ncompiler built on LLVM JIT** — scaled across SIMD lanes, CPU cores and processes.\n\nEvery header is small and heavily commented, with a runnable example and a test\nfor each topic. Together they walk from the basics of pricing to the kind of\njust-in-time, vectorized, distributed engines used in quantitative finance.\n\nThis is growing toward a single goal: a **scalable pricing \u0026 risk engine** where\nyou describe an instrument as a formula and get fast, correct results — on a\nlaptop or across a cluster. See [`ROADMAP.md`](ROADMAP.md) for the long-range plan.\n\n📖 **API reference:** https://nktkt.github.io/pricer/ (generated by Doxygen, published from CI).\n🏗️ **Design:** [`ARCHITECTURE.md`](ARCHITECTURE.md) · 🤝 **Contributing:** [`CONTRIBUTING.md`](CONTRIBUTING.md)\n\n## Project layout\n\n```\ninclude/pricer/   header-only core library\n  normal.hpp             standard-normal pdf / cdf\n  black_scholes.hpp      closed-form pricing + Greeks (continuous dividend yield q)\n  monte_carlo.hpp        generic terminal-value MC engine\n  american.hpp           American options: CRR binomial tree + Longstaff–Schwartz LSM\n  bermudan.hpp           Bermudan options: LSM over a finite (any) exercise schedule\n  basket.hpp             multi-asset options: basket (correlated GBM) + spread / exchange (Margrabe, Kirk)\n  digital.hpp            digital (binary) options: cash-or-nothing / asset-or-nothing (closed form + MC)\n  rainbow.hpp            rainbow (best-of / worst-of) two-asset options + bivariate normal CDF (Stulz)\n  bachelier.hpp          Bachelier (normal) model: pricing + Greeks + implied vol (handles negative rates)\n  exotics.hpp            path-dependent exotics: Asian / barrier / lookback (closed form + MC w/ BGK)\n  parallel.hpp           deterministic multithreaded MC (result independent of thread count)\n  rng.hpp                counter-based RNG (stateless) for parallel/SIMD path generation\n  simd.hpp               portable SIMD layer (GCC/Clang vector ext; vectorized exp/log/sqrt + inv-normal CDF)\n  simd_mc.hpp            SIMD-vectorized counter-based Monte Carlo (W paths per step)\n  parallel_simd.hpp      multicore + SIMD engine (deterministic, thread-count-independent)\n  variance_reduction.hpp antithetic \u0026 control-variate estimators\n  qmc.hpp                quasi-Monte Carlo (low-discrepancy) + inverse-normal CDF\n  implied_vol.hpp        implied-volatility solver (safeguarded Newton)\n  greeks_mc.hpp          Monte Carlo Greeks (bump+CRN and pathwise)\n  risk.hpp               Value-at-Risk / Expected Shortfall from a P\u0026L sample\n  xva.hpp                exposure-simulation scenario engine + CVA / DVA / bilateral CVA\n  curve.hpp              discount (yield) curve: zero rate / df / forward\n  vol_surface.hpp        implied-vol surface from a market price grid (bilinear)\n  smile.hpp              least-squares quadratic volatility-smile calibration\n  optimize.hpp           Levenberg–Marquardt least-squares solver (numerical Jacobian)\n  svi.hpp                SVI smile model + least-squares calibration (uses optimize.hpp)\n  quadrature.hpp         Gauss–Legendre numerical integration\n  heston.hpp             Heston stochastic-vol pricing (char. function) + calibration\n  sabr.hpp               SABR smile model: Hagan implied-vol + Black-76 + calibration + SDE MC\n  dual.hpp               forward-mode automatic differentiation (dual numbers)\n  greeks_ad.hpp          exact Black–Scholes Greeks via AD (matches closed form)\n  adjoint.hpp            reverse-mode AD (AAD): all first-order Greeks in one sweep\n  portfolio.hpp          book-level Greeks: a multi-name book's risk from one AAD sweep\n  local_vol.hpp          Dupire local volatility from a call-price surface\n  distributed.hpp        block-sharded MC with deterministic global aggregation\n  csv.hpp                minimal CSV read/write\n  market_data.hpp        CSV market-data adapters + result persistence\n  payoff_ast.hpp         payoff DSL: tokenizer, parser, typed AST + interpreter (no LLVM)\n  payoff_jit.hpp         walks the AST to emit LLVM IR and JIT-compile (needs LLVM)\nexamples/         runnable demos built on the library\ntests/            CTest suite (dependency-free; DSL test needs LLVM)\npython/           Python bindings (pybind11): `pip install .`\nserver/           REST pricing service (POSIX sockets, no dependencies)\n```\n\n| Example | Topic | Highlight |\n|---------|-------|-----------|\n| `black_scholes_demo` | Black–Scholes vs. Monte Carlo | Two independent methods agree to ~0.05% |\n| `convergence` | Accuracy vs. speed | Error shrinks like `1/sqrt(N)` (×100 paths → ~1/10 error) |\n| `parallel_mc` | Multithreaded Monte Carlo | ~8× faster across 10 logical cores |\n| `benchmark` | MC throughput harness | ~275 Mpaths/s; baseline for tracking optimizations |\n| `greeks` | Risk sensitivities (Greeks) | Closed-form vs. finite-difference cross-check |\n| `barrier_option` | Path-dependent product | Up-and-out barrier call via stepped MC |\n| `american_option` | Early-exercise options | American put: CRR binomial tree vs. Longstaff–Schwartz LSM; early-exercise premium |\n| `bermudan_option` | Finite exercise schedule | Bermudan put climbs from the European to the American value as exercise dates fill in |\n| `basket_options` | Multi-asset (correlated GBM) | Basket and spread/exchange options: geometric closed form, Margrabe \u0026 Kirk vs. Monte Carlo |\n| `digital_options` | Binary options | Cash-/asset-or-nothing closed form vs. MC; the exact vanilla decomposition |\n| `rainbow_options` | Two-asset best-of/worst-of | Stulz closed form for max/min options vs. MC; the call-max + call-min = two-vanillas parity |\n| `bachelier_demo` | Normal (Bachelier) model | Normal-vol pricing/Greeks/implied-vol vs. MC; prices a negative-rate floorlet |\n| `exotic_options` | Path-dependent exotics | Asian / barrier / lookback: each closed form vs. Monte Carlo (BGK-corrected) agree |\n| `payoff_interpret` | Payoff DSL without LLVM | Parse a formula → typed AST → tree-walking interpreter |\n| `jit_payoff` | **LLVM JIT** payoff compiler | Parses a formula string → LLVM IR → native code at runtime |\n| `path_dependent` | Exotics from formulas | Asian / barrier / lookback / digital, each a one-line formula |\n| `simd_payoff` | Vectorized codegen | Same formula compiled to `\u003cW x double\u003e` SIMD IR; scalar vs. batch |\n| `variance_reduction` | Fewer paths, same accuracy | Antithetic / control-variate / QMC error vs. plain MC |\n| `mc_greeks` | Monte Carlo Greeks | Bump+CRN and pathwise delta/vega vs. closed form |\n| `risk_demo` | Portfolio risk | 1-day VaR / ES of an option book by scenario simulation |\n| `pricer_cli` | Command-line tool | `price` / `iv` / `mc` sub-commands |\n| `vol_surface_demo` | Curve \u0026 vol surface | Discount curve, implied-vol surface, smile calibration |\n| `svi_calibration` | SVI model calibration | Fit an SVI smile to market quotes via Levenberg–Marquardt |\n| `heston_calibration` | Heston model calibration | Fit Heston to an option grid by least squares |\n| `sabr_smile` | SABR smile model | Hagan implied-vol smile, calibration round-trip, and an SDE Monte Carlo cross-check |\n| `ad_greeks` | Greeks by auto-diff | Forward-mode AD Greeks vs. closed form (machine precision) |\n| `aad_greeks` | Greeks by adjoint AD | All first-order Greeks from one reverse-mode backward sweep |\n| `local_vol_demo` | Dupire local vol | Local volatility recovered from a call-price surface |\n| `distributed_mc` | Distributed Monte Carlo | Sharded across worker processes; identical price for any worker count |\n| `market_data_demo` | Market data \u0026 persistence | Load quotes (CSV) → imply vol \u0026 Greeks → save results (CSV) |\n| `simd_paths` | SIMD path generation | Scalar vs. vectorized RNG: counter-based ~1.65×, SIMD ~2.2× over std::mt19937 |\n| `scale_benchmark` | Single-node speedup ladder | mt19937 → counter-based → SIMD → multicore; **~12.8× over the Phase-1 baseline** |\n| `xva_demo` | Counterparty risk (xVA) | Exposure profile → CVA / DVA / BCVA of a European call |\n| `portfolio_aad` | Book risk in one sweep | A multi-name book's value + every delta/vega from one reverse-mode AAD pass |\n\n## Requirements\n\n- CMake 3.16+ and a C++17 compiler (`clang++` or `g++`)\n- **LLVM** (optional, only for the `jit_payoff` / `path_dependent` examples and the\n  DSL test). On macOS: `brew install llvm`\n\n## Build, test \u0026 run\n\n```sh\ncmake -S . -B build -DCMAKE_BUILD_TYPE=Release\ncmake --build build --parallel\nctest --test-dir build --output-on-failure   # run the test suite\n./build/examples/black_scholes_demo          # run a demo\n```\n\nThe JIT examples are built when `PRICER_ENABLE_JIT` is on (default) **and** CMake\nfinds LLVM (point it there with `-DLLVM_DIR=$(llvm-config --cmakedir)` if needed);\notherwise they are skipped and the rest still builds. CI builds and tests on Linux\nand macOS with `-DPRICER_ENABLE_JIT=OFF` (the hosted runners expose an LLVM whose\nJIT cannot initialize there), so the JIT path is exercised locally instead.\n\n## The LLVM JIT highlight\n\n`jit_payoff` takes a payoff **formula as a string**, turns it into LLVM IR,\nJIT-compiles it to native code at runtime, and calls it from a Monte Carlo loop.\nChange the formula and you price a different instrument — without recompiling C++.\n\n```sh\n./build/examples/jit_payoff                            # call:  max(ST - K, 0)  (matches Black–Scholes)\n./build/examples/jit_payoff \"max(K - ST, 0)\"          # put\n./build/examples/jit_payoff \"max(ST-K,0)+max(K-ST,0)\" # straddle\n./build/examples/jit_payoff \"(ST \u003e K) * 10\"           # cash-or-nothing digital\n```\n\nFor `max(ST - K, 0)` (variables read from the `double* v` argument) it generates:\n\n```llvm\ndefine double @payoff_0(ptr %v) {\nentry:\n  %0 = getelementptr inbounds double, ptr %v, i64 0\n  %1 = load double, ptr %0, align 8        ; ST\n  %2 = getelementptr inbounds double, ptr %v, i64 1\n  %3 = load double, ptr %2, align 8        ; K\n  %4 = fsub double %1, %3\n  %5 = fcmp ogt double %4, 0.000000e+00\n  %6 = select i1 %5, double %4, double 0.000000e+00\n  ret double %6\n}\n```\n\n`path_dependent` goes further: the engine simulates whole paths and exposes\n`ST`, `avg`, `Smax`, `Smin` (plus `K`) to the DSL, so exotics become one-liners —\ne.g. an arithmetic Asian call is `max(avg - K, 0)` and an up-and-out barrier is\n`max(ST - K, 0) * (Smax \u003c 130)`.\n\nTwo more `PayoffJit` features round out the engine:\n\n- **Compiled-kernel cache** — `compile()` is keyed by `(width, variables, formula)`,\n  so repeating a formula returns the existing function pointer with no recompile.\n- **Vectorized codegen** — `compile_batch(formula, vars, W)` emits a\n  `void payoff_v(const double* v, double* out)` kernel that evaluates `W` paths at\n  once with `\u003cW x double\u003e` IR (see `simd_payoff`). The same parser produces scalar\n  or vector code; only the leaves (constant splats, vector loads) change.\n\nSupported grammar: numbers, variables (whichever names you bind), operators\n`+ - * /` and unary minus, comparisons `\u003c \u003e \u003c= \u003e= == !=` (yielding `1.0`/`0.0`),\nparentheses, and functions `exp log sqrt abs` (1 arg), `max min pow` (2 args),\n`if(cond, a, b)` (3 args).\n\n\u003e Note: the DSL parser uses exceptions for error reporting, so the `jit_payoff`\n\u003e target is compiled with `-fexceptions` (LLVM itself often ships built without).\n\n## Performance \u0026 scale\n\nReaching a target accuracy faster — by going wider (vectorize, then multicore),\nand by needing fewer paths:\n\n- **SIMD path generation** (`pricer/simd.hpp`, `pricer/simd_mc.hpp`) generates `W`\n  paths at a time on a portable vector layer built on the GCC/Clang vector\n  extensions (AVX2 on x86, NEON on ARM) — vectorized RNG, inverse-normal CDF and\n  `exp`. The counter-based RNG is what makes this possible: draw `i` is a pure\n  function of `(seed, i)`, so a batch of counters fills one SIMD register. ~2.2×\n  over a stateful `std::mt19937_64` baseline.\n- **Deterministic parallel MC** (`pricer/parallel.hpp`) splits work into a fixed\n  set of blocks with per-block seeds, so the result is bit-for-bit identical no\n  matter how many threads run it. Throughput scales with cores (~7× on 10 cores).\n- **Multicore + SIMD** (`pricer/parallel_simd.hpp`) stacks both axes: SIMD path\n  generation across all cores, still bit-identical for any thread count. The\n  `scale_benchmark` example walks the full ladder and reaches **~12.8× over the\n  single-threaded Phase-1 baseline on a 10-core machine** — clearing the roadmap's\n  ≥10× CPU target.\n- **Variance reduction** (`pricer/variance_reduction.hpp`, `pricer/qmc.hpp`):\n  antithetic and control-variate estimators, plus quasi-Monte Carlo whose error\n  decays roughly like `1/N` instead of `1/sqrt(N)`. For a vanilla call these reach\n  the analytic price with 1–3 orders of magnitude less error than plain MC at the\n  same path count (run `variance_reduction`).\n\nGPU offload (NVPTX/SPIR-V) is on the roadmap but needs appropriate hardware/CI,\nso it is not built here yet.\n\n## Risk \u0026 calibration\n\nFrom a price to the risk around it:\n\n- **Greeks, exactly.** Closed-form Black–Scholes Greeks, forward-mode AD\n  (`dual.hpp`, machine precision) and reverse-mode adjoint AD (`adjoint.hpp`) that\n  returns all first-order Greeks from one backward sweep. `portfolio.hpp` scales\n  that to a book: `book_greeks_aad` prices a multi-name book on one tape and\n  returns its value and every position's delta and vega from a single sweep — at a\n  cost independent of the number of inputs (`portfolio_aad`).\n- **Curves, surfaces \u0026 calibration.** A discount curve (`curve.hpp`), an\n  implied-vol surface (`vol_surface.hpp`), and least-squares calibration of a\n  quadratic smile, an SVI smile and the Heston model via Levenberg–Marquardt\n  (`optimize.hpp`, `smile.hpp`, `svi.hpp`, `heston.hpp`), plus Dupire local\n  volatility (`local_vol.hpp`).\n- **Portfolio risk \u0026 xVA.** Value-at-Risk / Expected Shortfall (`risk.hpp`), and\n  counterparty valuation adjustments (`xva.hpp`): a GBM exposure-simulation\n  scenario engine feeds CVA, DVA and bilateral CVA computed against a hazard-rate\n  survival curve and a discount curve (`xva_demo`).\n\n## Command line\n\n`pricer_cli` prices from the shell:\n\n```sh\n./build/examples/pricer_cli price --type call --S 100 --K 100 --r 0.05 --sigma 0.2 --T 1\n# price: 10.450584  (+ delta/gamma/vega/theta/rho)\n./build/examples/pricer_cli iv --type call --price 10.4506 --S 100 --K 100 --r 0.05 --T 1\n./build/examples/pricer_cli mc --type call --S 100 --K 100 --r 0.05 --sigma 0.2 --T 1 --paths 5000000\n```\n\n## REST service\n\nA dependency-free HTTP service (POSIX sockets) exposes pricing over the network,\nincluding an async Monte Carlo job API:\n\n```sh\ncmake -S . -B build -DPRICER_BUILD_SERVER=ON \u0026\u0026 cmake --build build\n./build/server/pricer_server 8080 \u0026\ncurl 'http://127.0.0.1:8080/price?type=call\u0026S=100\u0026K=100\u0026r=0.05\u0026sigma=0.2\u0026T=1'\n# {\"price\":10.45058357,\"delta\":0.63683065,...}\ncurl 'http://127.0.0.1:8080/submit?type=call\u0026S=100\u0026K=100\u0026r=0.05\u0026sigma=0.2\u0026T=1\u0026paths=20000000'  # {\"job_id\":1}\ncurl 'http://127.0.0.1:8080/job?id=1'   # {\"status\":\"done\",\"mc_price\":...}\ncurl 'http://127.0.0.1:8080/metrics'    # Prometheus counters: requests, latency, jobs, uptime\n```\n\nEndpoints: `/health`, `/price`, `/impliedvol`, `/mc`, `/submit`, `/job`, `/metrics`.\n**Observability:** `/metrics` exposes request, latency, job and uptime counters in\nthe Prometheus text format, and every request is written to stderr as a structured\nJSON access log line. `python server/smoke_test.py build/server/pricer_server` runs\nan end-to-end check (including `/metrics`).\n\n## Docker\n\nThe REST service ships as a container. A multi-stage build compiles it and the\nruntime image carries only the binary (the server has no third-party\ndependencies), running as a non-root user:\n\n```sh\ndocker build -t pricer-server .\ndocker run --rm -p 8080:8080 pricer-server\ncurl 'http://127.0.0.1:8080/price?type=call\u0026S=100\u0026K=100\u0026r=0.05\u0026sigma=0.2\u0026T=1'\ncurl http://127.0.0.1:8080/metrics\n# or, with compose:\ndocker compose up --build\n```\n\nA CI job builds the image and checks the container serves `/health`, `/price`\nand `/metrics`.\n\nFor Kubernetes, [`k8s/`](k8s/) has Deployment, Service and HorizontalPodAutoscaler\nmanifests (`kubectl apply -f k8s/`) — non-root pods, `/health` probes, CPU\nautoscaling, and Prometheus scrape annotations. See [`k8s/README.md`](k8s/README.md).\n\n## Python\n\nThe core is exposed to Python via pybind11 — price a book without touching C++:\n\n```sh\npip install .          # builds the C++ extension (scikit-build-core + pybind11)\npython python/example_book.py\n```\n\n```python\nimport pricer\nfrom pricer import OptionType\n\nc  = pricer.black_scholes_call(100, 100, 0.05, 0.20, 1.0)        # 10.4506\ng  = pricer.black_scholes_greeks(OptionType.Call, 100, 100, 0.05, 0.20, 1.0)\niv = pricer.implied_vol(OptionType.Call, c, 100, 100, 0.05, 1.0) # ~0.20\nmc = pricer.mc_price_parallel(OptionType.Call, 100, 100, 0.05, 0.20, 1.0, n_paths=10_000_000)\nrm = pricer.var_es(pnl_list, 0.99)                               # rm.var, rm.es\n```\n\n## Disclaimer\n\nEducational code. Not investment advice and not production-grade financial\nsoftware.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnktkt%2Fpricer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnktkt%2Fpricer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnktkt%2Fpricer/lists"}