defmodule ZenQuant.MixProject do use Mix.Project @version "0.2.0" @source_url "https://github.com/ZenHive/zen_quant" def project do [ app: :zen_quant, version: @version, elixir: "~> 1.18", start_permanent: Mix.env() == :prod, deps: deps(), aliases: aliases(), docs: docs(), package: package(), source_url: @source_url, dialyzer: dialyzer() ] end def cli do [preferred_envs: ["test.json": :test, "dialyzer.json": :dev]] end # Pure-function library: no supervision tree, no started processes. def application do [extra_applications: [:logger]] end defp deps do [ # Dev/test tooling {:ex_unit_json, "~> 0.6", only: [:dev, :test], runtime: false}, {:dialyzer_json, "~> 0.2", only: [:dev, :test], runtime: false}, {:styler, "~> 1.4", only: [:dev, :test], runtime: false}, {:credo, "~> 1.7", only: [:dev, :test], runtime: false}, {:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false}, {:ex_doc, "~> 0.40", only: :dev, runtime: false}, {:doctor, "~> 0.23", only: [:dev, :test], runtime: false}, {:sobelow, "~> 0.13", only: [:dev, :test], runtime: false}, {:ex_dna, "~> 1.5", only: [:dev, :test], runtime: false}, # 0.12.x series: reach pins ex_ast ~> 0.12.0 {:ex_ast, "~> 0.12.0", only: [:dev, :test], runtime: false}, # VibeKit baseline (credo/dialyxir/ex_dna above are shared with it) {:ex_slop, "~> 0.4", only: [:dev, :test], runtime: false}, {:reach, "~> 2.6", only: [:dev, :test], runtime: false}, {:vibe_kit, "~> 0.1", only: [:dev, :test], runtime: false}, # Tidewave for Claude Code MCP integration (non-Phoenix needs bandit) {:tidewave, "~> 0.5", only: :dev}, {:bandit, "~> 1.10", only: :dev}, # Self-describing API declarations {:descripex, "~> 0.6"}, # Integration tests + Tidewave exploration with real exchange data. # Local path dep: tracks the in-tree bourse checkout (../ccxt_client) # rather than a hex release, so zen_quant sees its current API during # co-development. Switch back to a `~>` hex pin before publishing. {:bourse, path: "../ccxt_client", only: [:dev, :test]} ] end defp aliases do [ tidewave: [ "run --no-halt -e 'Agent.start(fn -> Bandit.start_link(plug: Tidewave, port: 4011) end)'" ], "check.fast": [ "format --check-formatted", "compile --warnings-as-errors", "credo --strict --ignore TagTODO,TagFIXME" ], precommit: [ "format --check-formatted", "compile --warnings-as-errors", "credo --strict --ignore TagTODO,TagFIXME", "doctor --raise", # preferred_envs (cli/0) is ignored for alias steps — set MIX_ENV # explicitly. `mix cmd` runs System.cmd directly (no shell) on Elixir # 1.20+, so wrap in `sh -c` for the env-var prefix to take effect. "cmd sh -c 'MIX_ENV=test mix test.json --quiet --cover --cover-threshold 85 --summary-only --exclude integration'", "sobelow --skip" ], "precommit.full": ["precommit", "dialyzer.json --quiet"], # The harness `check_command` for this project. Every dispatched reviewer # runs this name, so it must exist here — a missing alias makes each run # burn a failed check and fall back to guessing the gate. "check.dispatch": ["precommit.full"], ci: [ "precommit.full", "ex_dna --max-clones 0", "reach.check --arch --smells", # Fail loudly if AGENTS.md drifted from CLAUDE.md (+ transitive # @-imports), so cross-family reviewers never gate against stale rules. # The sync script lives in the operator's claude-marketplace checkout; # on machines without it (CI runners, other clones) skip with a notice # instead of failing the gate on an unrelated missing file. ~s(cmd sh -c 'SYNC=$HOME/_DATA/code/claude-marketplace/scripts/sync-agents-md.sh; if [ -x "$SYNC" ]; then "$SYNC" --check; else echo "AGENTS.md freshness check SKIPPED: sync-agents-md.sh not found"; fi') ] ] end defp dialyzer do [ # OOM mitigation: skip transitive deps (default is :app_tree). # Tidewave/bandit's HTTP stack (plug, finch, mint, gun, cowlib, etc.) is # not in lib/ call graph and bloats the PLT to ~800 modules. plt_add_deps: :apps_direct, plt_add_apps: [:mix], # PLTs in priv/ survive `_build` wipes (avoids 5–10min rebuilds). plt_local_path: "priv/plts", plt_core_path: "priv/plts", ignore_warnings: ".dialyzer_ignore.exs" ] end defp package do [ description: "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.", licenses: ["MIT"], links: %{ "GitHub" => @source_url, "Changelog" => "https://hexdocs.pm/zen_quant/changelog.html" }, maintainers: ["ZenHive"], files: ~w(lib .formatter.exs mix.exs README.md CHANGELOG.md LICENSE SKILLS.md) ] end defp docs do [ main: "readme", extras: ["README.md", "SKILLS.md", "CHANGELOG.md"], source_url: @source_url, source_ref: "v#{@version}", groups_for_extras: [Guides: ["SKILLS.md"]], groups_for_modules: [ "Options — pricing & greeks": [ ZenQuant.Options.Pricing, ZenQuant.Greeks, ZenQuant.Options.Probability ], "Options — chain analytics": [ ZenQuant.Options, ZenQuant.Options.GammaWalls, ZenQuant.Options.Skew, ZenQuant.Options.Surface, ZenQuant.Options.ZeroDTE, ZenQuant.Options.BlockTrades, ZenQuant.Options.Chain, ZenQuant.Options.Snapshot, ZenQuant.Options.Deribit ], "Rates & carry": [ ZenQuant.Funding, ZenQuant.Basis ], "Volatility & statistics": [ ZenQuant.Volatility, ZenQuant.MeanReversion, ZenQuant.PowerLaw ], "Risk & sizing": [ ZenQuant.Risk, ZenQuant.Sizing, ZenQuant.Portfolio ], "Market microstructure": [ ZenQuant.Orderflow, ZenQuant.OrderBook, ZenQuant.MM, ZenQuant.Execution, ZenQuant.OrderState, ZenQuant.WS ], "Recording & backtesting": [ ZenQuant.Recorder.JSONL, ZenQuant.Recorder.Replay, ZenQuant.Backtest ], "Display helpers": [ ZenQuant.Helpers.Funding, ZenQuant.Helpers.Greeks, ZenQuant.Helpers.Risk ], "API discovery": [ ZenQuant.Manifest, Mix.Tasks.ZenQuant.Manifest ] ] ] end end