ZenQuant Skills Guide

Copy Markdown View Source

For agents and developers using ZenQuant as a library. Whether you're a Claude instance building a dashboard, a trading bot making decisions, or a human exploring the API — start here.

The 10-Second Discovery

ZenQuant.describe()

That's it. You'll get all discoverable modules with their purpose, namespace, function count, and whether they have full API annotations. Everything else flows from here.

Progressive Discovery

Three levels of detail, each building on the last:

# Level 1: What modules exist?
ZenQuant.describe()
# => [%{module: ZenQuant.Funding, short_name: :funding, function_count: ..., ...}, ...]

# Level 2: What functions does a module have?
ZenQuant.describe(:funding)
# => [%{name: :annualize, arity: 2, description: "Annualize a per-period...", spec: "..."}, ...]

# Level 3: Full detail for one function
ZenQuant.describe(:funding, :annualize)
# => %{name: :annualize, params: [%{name: :rate, kind: :value, ...}], returns: %{type: :float, ...}, returns_example: 0.1095, ...}

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

Machine-Readable Access

For runtime agents that need structured data (not formatted docs):

# Per-module hints (all functions)
ZenQuant.Funding.__api__()

# Per-function hints
ZenQuant.Funding.__api__(:annualize)

# Full manifest as map (all modules, all functions)
ZenQuant.Manifest.build()

Static export for non-BEAM consumers: mix zen_quant.manifestapi_manifest.json


What Can ZenQuant Do?

Navigate by intent — what question are you trying to answer?

"What's the market doing right now?"

ModuleShort NameKey Functions
ZenQuant.Execution:executionbest_price/2 (cross-venue BBO), from_multi/1 (Bourse.Multi bridge), best_for_side/2
ZenQuant.Orderflow:orderflowcvd/1 (total CVD), cvd_delta/1 (per-trade), vwap/1, imbalance/2 (orderbook), footprint_cells/3
ZenQuant.MM:mmfair_value/3 (micro-price from depth), spread_calculator/2 (Avellaneda-Stoikov)

"What are options telling us?"

ModuleShort NameKey Functions
ZenQuant.Options:optionsmax_pain/1, put_call_ratio/1, oi_by_strike/1, gex_by_strike/2, atm_iv/2, atm_iv_term_structure/3, moneyness_skew/3, pin_risk/3
ZenQuant.Options.Pricing:pricingprice/2, greeks/2, implied_volatility/3 (Black-Scholes-Merton — this is where pricing lives)
ZenQuant.Options.Surface:surfacebuild/1 (sparse IV surface from a chain)
ZenQuant.Options.Skew:skewterm_structure/1 (from normalized skew observations)
ZenQuant.Options.Probability:probabilityestimate/1 (terminal risk-neutral probability from a vertical)
ZenQuant.Options.Snapshot:snapshotto_report/2 (structured briefing; sections fail independently)
ZenQuant.Options.BlockTrades:block_tradessummarize/2 (aggressor side preserved, never inferred from size)
ZenQuant.Options.Deribit:deribitchain/3 (fetch + enrich), gamma_walls/3, dvol/3, parse_option/1
ZenQuant.Options.GammaWalls:gamma_wallsaggregate/3 (GEX walls with dealer/customer side)
ZenQuant.Options.ZeroDTE:zero_dtenear_strikes/3, theta_acceleration/3, gamma_exposure/3
ZenQuant.Greeks:greeksposition_greeks/1, dollar_delta/2, delta_neutral?/2, hedge_ratio/2 (aggregation only — for pricing use :pricing)

"How risky is this?"

ModuleShort NameKey Functions
ZenQuant.Risk:risksharpe_ratio/2, sortino_ratio/2, max_drawdown/1, var/2, portfolio_delta/1, check_limits/2
ZenQuant.Sizing:sizingfixed_fractional/3, kelly/2, volatility_scaled/4
ZenQuant.Portfolio:portfoliototal_exposure/1, unrealized_pnl/1, realized_pnl/1

"What's the yield?"

ModuleShort NameKey Functions
ZenQuant.Funding:fundingannualize/2, trend/2, mean_reversion_signal/2, rank/2, compare/1
ZenQuant.Basis:basisspot_perp/2, annualized/3, futures_curve/2, implied_funding/3

"What's BTC's valuation?"

ModuleShort NameKey Functions
ZenQuant.PowerLaw:power_lawfair_value/1, z_score/2, fair_value_range/1, forecast/2, classify/2

"Record and replay?"

ModuleShort NameKey Functions
ZenQuant.Recorder.JSONL:jsonlappend/3, stream/1, read_all/1, info/1
ZenQuant.Recorder.Replay:replaystream/2, run/3, reduce/4
ZenQuant.WS:wsstale_check/2, reconnect_metrics/2

Order lifecycle

ModuleShort NameKey Functions
ZenQuant.OrderState:order_statenew/2, on_fill/3, cancel/2, summary/1
ZenQuant.Volatility:volatilityrealized/2, parkinson/1, garman_klass/1, yang_zhang/1, iv_percentile/2

Module Short Name Reference

Discoverable modules, sorted alphabetically:

All 26 discoverable modules, sorted alphabetically. The function count is what ZenQuant.describe() reports.

Short NameModuleFnsPurpose
:basisZenQuant.Basis6Futures basis/premium, annualized carry
:block_tradesZenQuant.Options.BlockTrades1Block-trade aggregation (side never inferred)
:deribitZenQuant.Options.Deribit11Deribit symbol parsing, chain fetch, DVOL
:executionZenQuant.Execution4Cross-venue BBO, arb detection, split plans
:fundingZenQuant.Funding11Funding rates, APR/APY, trend, ranking
:gamma_wallsZenQuant.Options.GammaWalls1GEX wall computation
:greeksZenQuant.Greeks9Portfolio greeks aggregation/exposure (no pricing — see :pricing)
:jsonlZenQuant.Recorder.JSONL7JSONL snapshot recording
:mean_reversionZenQuant.MeanReversion1Ornstein-Uhlenbeck half-life estimation
:mmZenQuant.MM5Micro-price, optimal spread, fill rate
:optionsZenQuant.Options21Options chain analytics
:order_stateZenQuant.OrderState4Immutable order lifecycle
:orderflowZenQuant.Orderflow8CVD, VWAP, imbalance, footprint
:portfolioZenQuant.Portfolio4Portfolio exposure, PnL
:power_lawZenQuant.PowerLaw9Bitcoin power law model
:pricingZenQuant.Options.Pricing3Black-Scholes-Merton price, greeks, implied volatility
:probabilityZenQuant.Options.Probability1Risk-neutral probability from verticals
:replayZenQuant.Recorder.Replay3Filtered replay with speed control
:riskZenQuant.Risk13Sharpe, Sortino, VaR, drawdown, stress test
:sizingZenQuant.Sizing6Position sizing strategies
:skewZenQuant.Options.Skew1Skew term structures
:snapshotZenQuant.Options.Snapshot1Structured options briefing
:surfaceZenQuant.Options.Surface1Sparse implied-volatility surfaces
:volatilityZenQuant.Volatility11Realized, Parkinson, Garman-Klass, Yang-Zhang
:wsZenQuant.WS2WebSocket health monitoring
:zero_dteZenQuant.Options.ZeroDTE3Near-expiry (0DTE) analytics

Not in describe() (public but not Descripex-annotated): ZenQuant.Backtest, ZenQuant.OrderBook, ZenQuant.Manifest, and the three ZenQuant.Helpers.* formatting modules. | :zero_dte | ZenQuant.Options.ZeroDTE | 0DTE analytics |

Need live counts? Query runtime:

modules = ZenQuant.describe()
module_count = length(modules)
function_count = Enum.reduce(modules, 0, &(&1.function_count + &2))

Param Kinds

Every function parameter has a kind — this tells you where the data comes from:

  • :value — You provide this directly (a number, a date, a config option)
  • :exchange_data — Must be fetched from an exchange first

Exchange data params include a source: field telling you what to fetch:

ZenQuant.describe(:orderflow, :cvd_delta)
# => params: [%{name: :trades, kind: :exchange_data, source: "fetch_trades(symbol)", ...}]

ZenQuant.describe(:funding, :annualize)
# => params: [%{name: :rate, kind: :value, ...}, %{name: :period_hours, kind: :value, default: 8, ...}]

Common Composition Chains

Live Market Overview

Tickers from multiple exchanges → best price + funding + orderflow:

# 1. Build exchange handles, then fetch tickers from all of them
{:ok, bybit} = Bourse.exchange(:bybit)
{:ok, okx} = Bourse.exchange(:okx)
multi = Bourse.Multi.fetch_tickers([bybit, okx], "BTC/USDT:USDT")

# 2. Cross-venue price comparison
venues = ZenQuant.Execution.from_multi(Bourse.Multi.successes(multi))
bbo = ZenQuant.Execution.best_price(venues)
# => %{best_bid: %{price: 67801.0, exchange: "bybit"}, spread_bps: 0.74, ...}

# 3. Funding analysis
rates = [%{funding_rate: 0.0001}, %{funding_rate: 0.00015}]
ZenQuant.Funding.annualize(0.0001)        # => 10.95% APR
ZenQuant.Funding.trend(rates)             # => :rising | :falling | :stable

# 4. Orderflow from recent trades
{:ok, trades} = Bourse.fetch_trades(bybit, "BTC/USDT:USDT", limit: 100)
ZenQuant.Orderflow.vwap(trades)           # => volume-weighted average price

Options Analysis Pipeline

Chain → OI distribution → max pain → gamma walls:

# 1. Build the exchange handle, then fetch an enriched chain.
#    bourse: dispatcher module + %Bourse.Exchange{} handle, not a
#    per-exchange module. `chain/3` returns %{chain: ..., spot: ..., ...}.
{:ok, ex} = Bourse.exchange(:deribit)
{:ok, %{chain: chain, spot: spot}} =
  ZenQuant.Options.Deribit.chain(Bourse, ex, currency: "BTC", enrich: :greeks)

# 2. Open interest distribution
oi = ZenQuant.Options.oi_by_strike(chain)
{:ok, pain} = ZenQuant.Options.max_pain(chain)

# 3. Gamma walls (pure — from enriched chain)
walls = ZenQuant.Options.GammaWalls.aggregate(chain, spot)

# 4. Or use the composition shortcut
{:ok, result} = ZenQuant.Options.Deribit.gamma_walls(Bourse, ex, currency: "BTC")
# => %{walls: [...], spot: 67800.0, chain_size: 880, enriched_count: 50}

Pricing an Option Yourself

Options.Deribit reads greeks from the exchange. When you need to compute them — or back out implied volatility from a mark price — use ZenQuant.Options.Pricing, which is fully pure and needs no exchange at all:

inputs = %{
  spot: 67_800.0, strike: 70_000.0,
  time_to_expiry_years: 21 / 365,
  risk_free_rate: 0.05, dividend_yield: 0.0,
  volatility: 0.55
}

{:ok, price} = ZenQuant.Options.Pricing.price(:call, inputs)
{:ok, greeks} = ZenQuant.Options.Pricing.greeks(:call, inputs)

# Back out IV from an observed mark price (drop :volatility from the inputs)
{:ok, %{volatility: iv, converged: true}} =
  ZenQuant.Options.Pricing.implied_volatility(:call, 1_240.0, Map.delete(inputs, :volatility))

Time is a year fraction you compute — the module imposes no calendar or trading-day count.

Orderflow from Trades

Fetch → CVD + VWAP + footprint:

# Fetch trades (Bourse normalized — works directly)
{:ok, ex} = Bourse.exchange(:bybit)
{:ok, trades} = Bourse.fetch_trades(ex, "BTC/USDT:USDT", limit: 100)

# Total CVD for the list (single float)
total_cvd = ZenQuant.Orderflow.cvd(trades)

# Per-trade CVD contribution (single trade → single float)
delta = ZenQuant.Orderflow.cvd_delta(hd(trades))

# Aggregate metrics
vwap = ZenQuant.Orderflow.vwap(trades)

# Footprint: bucketed buy/sell volume (positional args: price_step, time_window_ms)
cells = ZenQuant.Orderflow.footprint_cells(trades, 100, 60_000)
# => [%{price_bucket: 67800.0, buy_volume: ..., sell_volume: ..., delta: ...}]

Gotchas

Things that trip up first-time users:

  1. cvd/1 vs cvd_delta/1cvd/1 takes a trade list and returns total CVD as a single float. cvd_delta/1 takes ONE trade and returns ONE float (its contribution). To sum a list, use cvd/1.

  2. dom_level/2 accepts orderbook directly — Pass either a wrapped map %{orderbook: ob, trades: trades} or an orderbook directly %{bids: [...], asks: [...]}. Both work. The bids/asks entries must be [price, size] tuples — which is exactly what a Bourse normalized OrderBook provides.

  3. IV is in percentage format — Deribit returns mark_iv: 48.6 (not 0.486). All ZenQuant IV functions expect and return percentages. expected_range/3 divides by 100 internally.

  4. fetch_trades takes keyword options — in bourse, fetch_trades(exchange, symbol, opts); since and limit are keywords, e.g. Bourse.fetch_trades(ex, "BTC/USDT", limit: 100). Only the symbol is positional.

  5. Networked functions take a dispatcher module AND an exchange handledvol/3, chain/3, and gamma_walls/3 are (dispatcher, exchange, opts): the top-level dispatcher module (Bourse) followed by a built %Bourse.Exchange{} handle from Bourse.exchange(:deribit). Per-exchange modules like Bourse.Deribit do not exist — passing one raises. These are the only functions in the whole library that touch the network; everything else is pure.

  6. Greeks aggregates, Options.Pricing computesZenQuant.Greeks takes greeks supplied by the exchange and sums/scales them. It contains no Black-Scholes. If you need a price, analytic greeks, or implied volatility, that's ZenQuant.Options.Pricing.

  7. All functions accept plain maps — ZenQuant has zero struct dependencies. Bourse normalized structs work because they're maps underneath. Raw exchange data works too (dual atom/string key access).

  8. Deribit fetch_ohlcv returns very few candles — With "1d" timeframe, Deribit returns only 1 candle. Use Bybit or Binance for historical OHLCV data.

  9. Bourse.Multi.fetch_tickers takes a single symbol string — Each exchange must support that exact symbol format. For cross-exchange with different conventions (e.g. Deribit "BTC-PERPETUAL" vs Bybit "BTC/USDT:USDT"), use the map form keyed by exchange handles: Bourse.Multi.fetch_tickers(%{bybit => "BTC/USDT:USDT", deribit => "BTC-PERPETUAL"}).

  10. Call Bourse.Multi.successes/1 before ZenQuant.Execution.from_multi/1Bourse.Multi returns %{%Bourse.Exchange{} => {:ok, ticker} | {:error, reason}}. Unwrap and filter errors first: multi |> Bourse.Multi.successes() |> ZenQuant.Execution.from_multi().


The bourse Connection

ZenQuant is the computation layer. bourse provides the data. Here's what feeds what:

bourse FunctionZenQuant DomainExample
fetch_tickers / Multi.fetch_tickersExecution, MMfrom_multi → best_price
fetch_tradesOrderflowcvd_delta, vwap, footprint_cells
fetch_order_bookOrderflow, MMimbalance, heatmap_points, fair_value
fetch_funding_ratesFundingannualize, trend, rank
fetch_option_chainOptionsoi_by_strike, max_pain, gex_by_strike
fetch_greeksOptions, Greekschain/2 enrichment, position_greeks
fetch_volatility_historyOptions.Deribitdvol/2
fetch_ticker (spot + futures)Basisspot_perp, annualized
Historical closesVolatilityrealized, parkinson, yang_zhang
Position dataRisk, Portfolio, Sizingportfolio_delta, total_exposure, kelly

Static Manifest

For non-BEAM consumers (Python, TypeScript, Rust, HTTP agents):

mix zen_quant.manifest
# => Writes api_manifest.json

The manifest includes every module, function, param (with kinds and descriptions), return type, returns_example, composition hints (composes_with), and error cases. It's the same data as ZenQuant.Manifest.build() serialized to JSON.

Use this for:

  • HTTP API endpoint generation
  • Agent capability registration (EIP-8004)
  • SDK generation for other languages
  • CI validation that the API contract hasn't changed unexpectedly