Packages

Pure-function trading analytics for Elixir — Black-Scholes pricing and IV, options chain analytics (max pain, GEX, skew, surfaces), funding rates, basis, volatility estimators, risk metrics, position sizing, orderflow, and portfolio analytics. Self-describing for AI agents.

Current section

Files

Jump to
zen_quant README.md
Raw

README.md

# ZenQuant
[![Hex.pm](https://img.shields.io/hexpm/v/zen_quant.svg)](https://hex.pm/packages/zen_quant)
[![Docs](https://img.shields.io/badge/hex-docs-blue.svg)](https://hexdocs.pm/zen_quant)
[![License](https://img.shields.io/hexpm/l/zen_quant.svg)](https://github.com/ZenHive/zen_quant/blob/HEAD/LICENSE)
Trading analytics for Elixir, as pure functions.
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.
Every function takes plain maps and numbers and returns plain maps and tuples.
No processes, no supervision tree, no I/O, no exchange client. You fetch the
data however you like; ZenQuant does the math.
The only runtime dependency is [Descripex](https://hex.pm/packages/descripex),
which powers the self-describing API surface.
## Installation
```elixir
def deps do
[
{:zen_quant, "~> 0.2.0"}
]
end
```
Requires Elixir ~> 1.18.
## Quick Start
Every output below is a real return value, not a sketch.
### Options pricing and greeks
```elixir
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
```elixir
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
```elixir
# 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
```elixir
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
```elixir
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
```elixir
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:
```elixir
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:
```elixir
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`.
[SKILLS.md](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.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.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 |
### Risk & sizing
| Module | Purpose |
|--------|---------|
| `ZenQuant.Risk` | Sharpe, Sortino, Calmar, VaR, max drawdown, beta, concentration, stress test, liquidation headroom |
| `ZenQuant.Sizing` | Kelly, fixed fractional, optimal f, 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
- **Pure functions only** — no GenServers, no state, no side effects, no started
application. Same inputs, same outputs, always.
- **Plain maps in, plain maps out** — no struct dependencies. Exchange payloads
work as-is because Bourse structs are maps.
- **Structured returns, never strings** — consistent `{:ok, result}` /
`{:error, reason}` contracts. Formatting lives in `Helpers.*`.
- **No venue constants** — anything that varies by exchange, fee tier, or
market regime is a caller-supplied parameter, not a baked-in value. Funding
cadence is an argument, not an assumption.
- **Self-describing** — every function carries machine-readable hints via
Descripex, so agents discover the API at runtime.
- **Determinism as a feature**`ZenQuant.Backtest` over recorded JSONL gives
same data + same strategy = same result, which is what trustless
re-execution (EIP-8004) needs.
## 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](SKILLS.md) for composition chains,
the `:value` vs `:exchange_data` param distinction, and known gotchas.
## License
MIT — see [LICENSE](https://github.com/ZenHive/zen_quant/blob/HEAD/LICENSE).