ZenQuant

Hex.pm Docs License

Trading analytics for Elixir.

Black-Scholes pricing and implied volatility, options chain analytics (max pain, GEX walls, skew term structures, IV surfaces), funding rates, cash-and-carry basis, volatility estimators, risk metrics, position sizing, orderflow, and portfolio aggregation.

Calculations take plain maps and numbers and return plain maps and tuples. The library starts no processes or supervision tree and needs no exchange client. You fetch the data however you like; ZenQuant does the math. Clock-convenience arities such as Options.time_to_expiry/1 and Options.session_phase/1 read the wall clock; pass an explicit reference DateTime to their two-argument forms for deterministic calculations. Recorder modules read and write JSONL files.

The only runtime dependency is Descripex, which powers the self-describing API surface.

Installation

def deps do
[
{:zen_quant, "~> 0.11.2"}
]
end

Requires Elixir ~> 1.18.

Quick Start

Every output below is a real return value, not a sketch.

Options pricing and greeks

inputs = %{
spot: 100.0,
strike: 100.0,
time_to_expiry_years: 0.25,
risk_free_rate: 0.05,
dividend_yield: 0.0,
volatility: 0.6
}
ZenQuant.Options.Pricing.price(:call, inputs)
# => {:ok, 12.480797612835424}
ZenQuant.Options.Pricing.greeks(:call, inputs)
# => {:ok, %{delta: 0.5759983410021886, gamma: 0.0130560458310534,
# vega: 19.5840687465801, theta: -25.756834320265295, rho: 11.27975912184586}}
# Solve for the volatility that reproduces a market price
ZenQuant.Options.Pricing.implied_volatility(:call, 12.5, Map.delete(inputs, :volatility))
# => {:ok, %{volatility: 0.6009805272333324, iterations: 31,
# residual: -4.5e-10, converged: true}}

Time is supplied directly in years — the module applies no calendar or trading-day count, so you choose the convention.

Options chain analytics

chain = %{
"BTC-31JAN26-84000-C" => %{open_interest: 100.0, raw: %{"gamma" => 0.00001}}
}
ZenQuant.Options.oi_by_strike(chain) # => %{84000.0 => 100.0}
ZenQuant.Options.max_pain(chain) # => {:ok, 84000.0}

Plus gex_by_strike/2, pin_magnets/2, gamma_flip/2, put_call_ratio/1, atm_iv/2, atm_iv_term_structure/3, moneyness_skew/3, expected_range/3, and ZenQuant.Options.Surface.build/1 for sparse IV surfaces.

Funding and basis

# Per-period funding rate -> annualized APR. The period is a caller argument,
# so venues with non-8h cadence are a value, not a hardcoded constant.
ZenQuant.Funding.annualize(0.0001) # => 0.1095 (10.95% APR at 8h funding)
ZenQuant.Funding.annualize(0.0001, 1) # => 0.876 (hourly funding)
ZenQuant.Basis.spot_perp(100_000.0, 100_250.0)
# => %{absolute: 250.0, percent: 0.25, direction: :contango}
ZenQuant.Basis.annualized(100_000.0, 100_250.0, 30)
# => 0.030416666666666665

Volatility

ZenQuant.Volatility.realized([100.0, 101.5, 99.8, 102.0, 100.5])
# => 0.019963912649687825
ZenQuant.MeanReversion.half_life([100.0, 101.0, 99.5, 100.2, 100.8, 99.9, 100.1])
# => {:ok, 0.448506999188226}

Also parkinson/1, garman_klass/1, yang_zhang/1, cone/2, iv_rank/2, iv_percentile/2, iv_vs_rv/2.

Portfolio greeks, risk, and sizing

positions = [%{delta: 0.5, gamma: 0.02, theta: -10.0, vega: 25.0, quantity: 10}]
ZenQuant.Greeks.position_greeks(positions)
# => %{delta: 5.0, gamma: 0.2, theta: -100.0, vega: 250.0}
ZenQuant.Risk.sharpe_ratio([0.01, -0.005, 0.02, 0.015, -0.01])
# => 0.4636004455717535
ZenQuant.Risk.max_position_size(100_000, risk_pct: 0.02, stop_distance_pct: 0.05)
# => 10000.0
ZenQuant.Sizing.kelly(0.6, 2.0) # => 0.19999999999999998

Orderflow

ZenQuant.Orderflow.cvd([%{side: "buy", amount: 1.5}, %{side: "sell", amount: 0.5}])
# => 1.0

Also cvd_delta/1 (per-trade series), vwap/1, imbalance/2, heatmap_points/2, footprint_cells/3, dom_level/2.

API Discovery

ZenQuant describes itself. You do not need to read the docs to find a function:

ZenQuant.describe() # 26 modules with purpose + function count
ZenQuant.describe(:funding) # every function in ZenQuant.Funding
ZenQuant.describe(:funding, :annualize) # params, returns, returns_example, errors, composes_with

Short names are the last module segment, lowercased: ZenQuant.PowerLaw → :power_law, ZenQuant.Options.Deribit → :deribit.

For runtime agents that want structured data rather than formatted docs:

ZenQuant.Funding.__api__() # all hints for a module
ZenQuant.Funding.__api__(:annualize) # hints for one function
ZenQuant.Manifest.build() # full manifest as a map

Static export for non-BEAM consumers: mix zen_quant.manifest → api_manifest.json.

Chain scope hints

Parameter hints shown by ZenQuant.describe/2 and the manifest use :scope to describe how an operation handles chain boundaries. The two supported shapes are scope: :single (a bare atom, with no reason) and scope: %{mode: mode, reason: reason} for the other modes below. reason must be a non-empty string explaining why that parameter uses the alternative policy.

All :exchange_data parameters in ZenQuant.Options and its submodules require an explicit scope policy. Outside that namespace, declare shape: :option_chain on chain inputs (as in ZenQuant.Greeks.from_chain/2) to require the same check. Membership uses this structured hint, never the source or description text; other exchange-data inputs in the same module need no chain-scope declaration.

Mode Meaning
:single Requires one underlying and settlement regime across all parseable legs. The function calls ZenQuant.Options.Internal.validate_chain_scope/1 before filtering legs and returns {:error, :heterogeneous_chain} for a mixed-underlying or mixed-settlement chain.
:per_leg Processes, filters, or compares legs independently without aggregating across legs; mixed chains are allowed.
:cross_chain Combines multiple chains because the caller explicitly requests aggregation across chains.
:per_section Validates chain scope within each report section; sections report :heterogeneous_chain independently.
:not_chain The parameter contains other exchange data, not an option chain; chain-scope validation does not apply.

For example, ZenQuant.describe(:options, :gex_by_strike) shows scope: :single on chain: mixing BTC and ETH, or BTC and BTC_USDC option symbols, produces {:error, :heterogeneous_chain} even if a mismatched leg would otherwise be filtered out.

SKILLS.md is the full agent guide — composition chains, param kinds, and gotchas.

Modules

Options — pricing & greeks

Module Purpose
ZenQuant.Options.Pricing Black-Scholes-Merton price, analytic greeks, implied volatility solver
ZenQuant.Options.Pricing.Lattice Cox-Ross-Rubinstein binomial lattice for vanilla European and American options
ZenQuant.Options.Pricing.FiniteDifference θ-scheme PDE pricer (Crank-Nicolson default) for vanilla European and American options
ZenQuant.Options.ExpiryGreeks Explicit per-contract finite-difference delta/gamma snapshot proxies
ZenQuant.Options.ModelDeviation Neutral quote/model deviation scans with explicit costs, thresholds, and volatility provenance
ZenQuant.Greeks Portfolio greeks aggregation and exposure — aggregates greeks from exchange data, does not compute them
ZenQuant.Options.Probability Terminal risk-neutral probabilities from vertical spreads

Options — chain analytics

Module Purpose
ZenQuant.Options Max pain, GEX by strike, pin magnets, gamma flip, OI, ATM IV, expected range
ZenQuant.Options.GammaWalls GEX wall computation with explicit dealer/customer side convention
ZenQuant.Options.Skew Deterministic term structures from normalized skew observations
ZenQuant.Options.Surface Sparse implied-volatility surfaces from a pre-fetched chain
ZenQuant.Options.ZeroDTE Near-expiry analytics: near strikes, theta acceleration, gamma exposure
ZenQuant.Options.BlockTrades Block-trade aggregation — aggressor side preserved, never inferred from size
ZenQuant.Options.Snapshot Structured options briefing over a chain; sections fail independently
ZenQuant.Options.Chain Canonical chain from a fetched payload; greeks enrichment from IV, no I/O
ZenQuant.Options.Deribit Deribit symbol parsing, chain fetch + enrich, DVOL, gamma walls

Rates & carry

Module Purpose
ZenQuant.Funding Funding rates: annualized APR, trend, spikes, ranking, carry candidates
ZenQuant.Basis Spot/perp basis, annualized carry, futures curve, implied funding

Volatility & statistics

Module Purpose
ZenQuant.Volatility Realized, Parkinson, Garman-Klass, Yang-Zhang, cones, IV rank/percentile
ZenQuant.MeanReversion Ornstein-Uhlenbeck half-life estimation
ZenQuant.PowerLaw Bitcoin power law: fair value, z-score, support/resistance bands, forecast

Trend & momentum

Module Purpose
ZenQuant.Trend Pivot-envelope range geometry per window; rate of change, RSI, StochRSI, DSS Bressert
ZenQuant.Fisher Fisher Transform oscillator and its one-bar-delayed trigger line

Risk & sizing

Module Purpose
ZenQuant.Risk Sharpe, Sortino, Calmar, VaR, max drawdown, beta, correlation, concentration, stress test, liquidation headroom
ZenQuant.Sizing Kelly, fixed fractional, optimal f, compounding growth, anti-martingale, volatility-scaled
ZenQuant.Portfolio Exposure aggregation, realized/unrealized PnL, position summaries

Market microstructure

Module Purpose
ZenQuant.Orderflow CVD, VWAP, imbalance, heatmap, footprint, DOM level
ZenQuant.OrderBook Shared order-book level contract — strict parsing, explicit errors on unknown shapes
ZenQuant.MM Market making: fair value, inventory skew, spread calculator, fill rate
ZenQuant.Execution Cross-venue best price, arbitrage detection, non-executing order split plans
ZenQuant.OrderState Immutable order lifecycle: fills, VWAP accumulation, cancellation
ZenQuant.WS WebSocket health: stale check, reconnect metrics

Recording & backtesting

Module Purpose
ZenQuant.Recorder.JSONL JSONL snapshot recording — encode, decode, append, stream, read_all
ZenQuant.Recorder.Replay Filtered replay with optional speed delays
ZenQuant.Backtest Deterministic strategy evaluation over recorded data

Display helpers

ZenQuant.Helpers.Funding, ZenQuant.Helpers.Greeks, ZenQuant.Helpers.Risk — formatting for dashboards. Core modules never format for display.

Design Principles

For AI Agents

ZenQuant is built agents-first. Start with ZenQuant.describe(), then narrow with describe/1 and describe/2. All returns are structured data that composes directly into the next call. See SKILLS.md for composition chains, the :value vs :exchange_data param distinction, and known gotchas.

License

MIT — see LICENSE.