266 lines
14 KiB
Markdown
266 lines
14 KiB
Markdown
# zfin
|
|
|
|
A financial data library, CLI, and terminal UI written in Zig. Tracks portfolios, analyzes trailing returns, displays options chains and earnings history, and renders price and projection charts (inline Kitty graphics or PNG export) -- all from the terminal.
|
|
|
|
## Quick start
|
|
|
|
### macOS (Apple Silicon) -- pre-built binary
|
|
|
|
```bash
|
|
# Download the latest aarch64-macos binary, make it executable, drop on $PATH
|
|
curl -L -o zfin \
|
|
https://git.lerch.org/api/packages/lobo/generic/zfin-aarch64-macos/latest/zfin-aarch64-macos
|
|
chmod +x zfin
|
|
sudo mv zfin /usr/local/bin/ # or anywhere on $PATH
|
|
```
|
|
|
|
### Linux / building from source
|
|
|
|
```bash
|
|
# Requires Zig 0.16.0. With mise installed, `mise install` will pick the right
|
|
# version from .mise.toml. Otherwise grab Zig 0.16.0 manually.
|
|
zig build
|
|
# Binary lands at zig-out/bin/zfin
|
|
```
|
|
|
|
### Configure
|
|
|
|
```bash
|
|
# Set at least one API key plus your contact email (see "API keys" below).
|
|
# The email is required for ETF profiles + `enrich` because SEC EDGAR mandates
|
|
# a User-Agent contact.
|
|
export TIINGO_API_KEY=your_key
|
|
export ZFIN_USER_EMAIL=you@example.com
|
|
```
|
|
|
|
### Use it
|
|
|
|
```bash
|
|
zfin perf VTI # trailing returns
|
|
zfin quote AAPL # real-time quote
|
|
zfin options AAPL # options chains
|
|
zfin earnings MSFT # earnings history
|
|
zfin portfolio # portfolio summary (reads portfolio.srf)
|
|
zfin analysis # portfolio analysis (reads portfolio.srf + metadata.srf)
|
|
|
|
# Interactive TUI
|
|
zfin i # auto-loads portfolio.srf from cwd
|
|
zfin i -p portfolio.srf -w watchlist.srf
|
|
zfin i -s AAPL # start with a symbol, no portfolio
|
|
```
|
|
|
|
Building from source requires Zig 0.16.0.
|
|
|
|
## Documentation
|
|
|
|
Full user documentation lives in [`docs/`](docs/README.md) and is built
|
|
around the runnable example portfolios in [`examples/`](examples/).
|
|
|
|
- **New here?** [Getting started](docs/getting-started.md) -- install,
|
|
configure, and run your first commands.
|
|
- **Learn the model:** [Core concepts](docs/explanation/concepts.md).
|
|
- **Do a task (the user manual):**
|
|
- [Build your portfolio](docs/guides/set-up-your-portfolio.md)
|
|
- [Classify your holdings](docs/guides/classify-holdings.md) and [map your accounts](docs/guides/set-up-accounts.md)
|
|
- [Read your portfolio](docs/guides/read-your-portfolio.md)
|
|
- [Plan for retirement](docs/guides/plan-retirement.md)
|
|
- [Snapshots and history](docs/guides/snapshots-and-history.md), [track contributions](docs/guides/track-contributions.md)
|
|
- [Audit against your brokerage](docs/guides/audit-against-brokerage.md)
|
|
- [Customize the TUI](docs/guides/customize-the-tui.md), [offline use and refreshing data](docs/guides/offline-and-refresh.md)
|
|
- **Look something up (reference):**
|
|
- [CLI commands](docs/reference/cli/index.md)
|
|
- Config files: [`portfolio.srf`](docs/reference/config/portfolio-srf.md), [`accounts.srf`](docs/reference/config/accounts-srf.md), [`metadata.srf`](docs/reference/config/metadata-srf.md), [`projections.srf`](docs/reference/config/projections-srf.md), and [more](docs/reference/config/environment.md)
|
|
- [The interactive TUI](docs/reference/tui.md)
|
|
- [Data providers and API keys](docs/reference/providers.md)
|
|
- **Understand the why (explanation):** [caching](docs/explanation/caching.md), [data providers](docs/explanation/data-providers.md), [returns](docs/explanation/returns-and-performance.md), [the projection model](docs/explanation/projections-model.md), [FAQ](docs/explanation/faq-troubleshooting.md).
|
|
|
|
## Data providers
|
|
|
|
zfin aggregates data from multiple free-tier APIs, using each for what
|
|
it does best, with aggressive caching to stay within free-tier limits.
|
|
|
|
| Data type | Provider | Auth | Free-tier limit | Cache TTL |
|
|
|-----------------------|------------------|----------------------|----------------------------|--------------|
|
|
| Daily candles (OHLCV) | Tiingo | `TIINGO_API_KEY` | 1,000 req/day, 50 req/hour | 23h45m |
|
|
| Real-time quotes | Yahoo Finance | None required | Unofficial | Never cached |
|
|
| Quote fallback | TwelveData | `TWELVEDATA_API_KEY` | 8 req/min, 800/day | Never cached |
|
|
| Dividends | Polygon | `POLYGON_API_KEY` | 5 req/min | 14 days |
|
|
| Splits | Polygon | `POLYGON_API_KEY` | 5 req/min | 14 days |
|
|
| Options chains | CBOE | None required | ~30 req/min (self-imposed) | 1 hour |
|
|
| Earnings | FMP | `FMP_API_KEY` | 250 req/day | 30 days* |
|
|
| ETF profiles | SEC EDGAR | `ZFIN_USER_EMAIL` | 10 req/sec | ~90 days |
|
|
| Classification | Wikidata + EDGAR | `ZFIN_USER_EMAIL` | No per-day quota | Long-lived |
|
|
|
|
Not all keys are required; without a given key, that data type is
|
|
simply unavailable. For per-provider notes, signup links, the full
|
|
caching/TTL model, and the complete environment-variable list, see
|
|
[Data providers and API keys](docs/reference/providers.md),
|
|
[Caching and data freshness](docs/explanation/caching.md), and
|
|
[Environment variables](docs/reference/config/environment.md).
|
|
|
|
## Architecture
|
|
|
|
```
|
|
src/
|
|
main.zig CLI dispatch + TUI entry point
|
|
root.zig Library root, exports all public types
|
|
Config.zig Configuration from env vars / .env files
|
|
service.zig DataService: cache-check -> fetch -> cache -> return
|
|
Date.zig Date type (i32 days since epoch); arithmetic, formatting
|
|
Money.zig Money formatting ($1,234.56; whole/trim/signed variants)
|
|
format.zig Shared formatters, braille engine, ANSI helpers
|
|
padded.zig Generic width/alignment wrapper for any formattable type
|
|
PortfolioData.zig Per-portfolio parse, price fetch, summary, background workers
|
|
portfolio_loader.zig Loads + union-merges portfolio_*.srf for CLI and TUI
|
|
compare.zig Composes two points in time (snapshot vs live) for `compare`
|
|
history.zig Reads history/<date>-portfolio.srf snapshots; aggregation
|
|
market.zig Market calendar; close-anchored cache freshness
|
|
git.zig Thin git subprocess wrappers (diff/walk tracked portfolios)
|
|
atomic.zig Crash-safe atomic file writes (tmp + fsync + rename)
|
|
stderr.zig Best-effort (non-throwing) stderr hint/progress writers
|
|
term_graphics.zig Kitty graphics emission for plain (non-TUI) CLI charts
|
|
term_query.zig Terminal capability probing for inline CLI graphics
|
|
chart_export.zig Chart-to-PNG export (`--export-chart <path>`)
|
|
comptime_validator.zig Shared comptime contract-validation helpers
|
|
models/
|
|
candle.zig OHLCV price bars
|
|
quote.zig Real-time quote data
|
|
dividend.zig Dividend records with type classification
|
|
split.zig Stock splits
|
|
option.zig Option contracts and chains
|
|
earnings.zig Earnings events with surprise calculation
|
|
etf_profile.zig ETF profiles with holdings and sectors
|
|
portfolio.zig Lots, positions, and portfolio aggregation
|
|
classification.zig Classification metadata parser
|
|
snapshot.zig Snapshot record types (history/*-portfolio.srf wire format)
|
|
transaction_log.zig transaction_log.srf wire format (transfer:: records)
|
|
shiller_year.zig One year of Shiller returns (S&P / bond / CPI)
|
|
providers/
|
|
tiingo.zig Tiingo: daily candles (primary)
|
|
yahoo.zig Yahoo Finance: quotes (primary), candle fallback
|
|
twelvedata.zig TwelveData: quote fallback
|
|
polygon.zig Polygon: dividends, splits (with forward-looking entries)
|
|
fmp.zig FMP: earnings (actuals + estimates)
|
|
cboe.zig CBOE: options chains (no API key)
|
|
Edgar.zig SEC EDGAR: ETF profiles (NPORT-P), ticker map, XBRL facts
|
|
Wikidata.zig Wikidata SPARQL: classification metadata for `enrich`
|
|
openfigi.zig OpenFIGI: CUSIP to ticker lookup
|
|
json_utils.zig Shared JSON parsing helpers
|
|
xml.zig Vendored XML parser used by Edgar.zig (see "Vendored code")
|
|
analytics/
|
|
indicators.zig SMA, Bollinger Bands, RSI
|
|
performance.zig Trailing returns (as-of-date + month-end)
|
|
risk.zig Per-symbol volatility, Sharpe, drawdown
|
|
portfolio_risk.zig Correlation-aware portfolio-level risk
|
|
valuation.zig Portfolio summary, allocations, covered-call adjustments
|
|
analysis.zig Breakdowns by class/sector/geo/account/tax
|
|
benchmark.zig Per-position benchmark returns
|
|
exposure.zig Look-through exposure (direct + ETF holdings)
|
|
milestones.zig Retirement-attainment grid
|
|
projections.zig Monte Carlo + percentile bands
|
|
forecast_evaluation.zig Forecast-vs-actual (convergence + return back-test)
|
|
observations.zig Portfolio sanity checks for the review surface
|
|
timeline.zig History-tab tier rollup
|
|
data/
|
|
ie_data.csv Raw Shiller S&P 500 + CPI dataset (CSV)
|
|
shiller.zig Shiller series, comptime-generated from ie_data.csv
|
|
imported_values.zig Back-history portfolio values (imported from spreadsheet)
|
|
staleness.zig Account-cadence staleness checks
|
|
Journal.zig acknowledgments.srf journal (acknowledged findings)
|
|
brokerage/
|
|
types.zig Normalized BrokeragePosition record + dollar parser
|
|
fidelity.zig Fidelity "Download Positions" CSV parser
|
|
schwab.zig Schwab export parser
|
|
wells_fargo.zig Wells Fargo export parser
|
|
views/
|
|
portfolio_sections.zig Portfolio view model (renderer-agnostic StyleIntent)
|
|
compare.zig Compare view model
|
|
history.zig History view model
|
|
projections.zig Projections view model
|
|
review.zig Per-holding performance + risk dashboard view model
|
|
observations_view.zig Findings list (engine output + acknowledgments)
|
|
cache/
|
|
store.zig SRF file cache with TTL freshness checks
|
|
net/
|
|
http.zig HTTP client with retries and server-error retry
|
|
RateLimiter.zig Token-bucket rate limiter
|
|
commands/
|
|
common.zig Shared CLI helpers (progress, formatting, color)
|
|
framework.zig CLI command framework + portfolio pattern resolution
|
|
TimeRange.zig Shared --range / time-window parsing
|
|
audit/ Brokerage reconciliation helpers for `audit`
|
|
(one .zig per CLI command: perf, quote, portfolio, analysis, history, ...)
|
|
charts/
|
|
chart.zig Price + Bollinger + volume + RSI renderer (z2d)
|
|
projection_chart.zig Percentile-band + median projection renderer
|
|
forecast_chart.zig Convergence / back-test line renderer
|
|
line_chart.zig Single-series value-over-time line renderer
|
|
axis.zig Shared axis-label helpers
|
|
draw.zig Shared z2d drawing primitives
|
|
text.zig 5x7 bitmap font for axis labels
|
|
tui.zig Interactive TUI App orchestrator
|
|
tui/
|
|
tab_framework.zig Tab contract + comptime validator
|
|
keybinds.zig Configurable keybinding system
|
|
theme.zig Configurable color theme
|
|
input_buffer.zig Modal text-input state machine
|
|
(nine *_tab.zig tabs: portfolio, analysis, review, projections,
|
|
history, quote, performance, earnings, options)
|
|
version.zig Build version string
|
|
```
|
|
|
|
Data files (user-managed, auto-detected in cwd or `$ZFIN_HOME`):
|
|
```
|
|
portfolio.srf Portfolio lots (positions and cost basis)
|
|
accounts.srf Account -> tax-type mapping for analysis
|
|
metadata.srf Per-symbol classification (class/sector/geo) for analysis
|
|
watchlist.srf Symbols to track in the TUI watchlist
|
|
transaction_log.srf Declared transfers so contributions math skips internal moves
|
|
projections.srf Retirement / Monte Carlo projection assumptions
|
|
theme.srf TUI color theme overrides
|
|
keys.srf TUI keybinding overrides
|
|
acknowledgments.srf Acknowledged review-tab findings (app-maintained)
|
|
```
|
|
|
|
### Dependencies
|
|
|
|
| Dependency | Source | Purpose |
|
|
|----------------------------------------------------|---------------|---------------------------------------------------|
|
|
| [SRF](https://git.lerch.org/lobo/srf) | Git | Cache file format and portfolio/watchlist parsing |
|
|
| [libvaxis](https://github.com/rockorager/libvaxis) | Git (v0.6.0) | Terminal UI rendering |
|
|
| [z2d](https://github.com/vancluever/z2d) | Git (v0.11.0) | Pixel chart rendering (Kitty graphics protocol) |
|
|
| [zeit](https://github.com/rockorager/zeit) | Git (v0.9.0) | Calendar arithmetic + timezone conversion (ET) |
|
|
|
|
## Building
|
|
|
|
Requires Zig 0.16.0. The canonical version is pinned in `.mise.toml`:
|
|
|
|
```bash
|
|
# Recommended: use mise to install the right Zig and ZLS automatically
|
|
mise install
|
|
|
|
# Or, with Zig 0.16.0 already on PATH:
|
|
zig build # build the zfin binary
|
|
zig build test # run all tests
|
|
zig build run -- <args> # build and run
|
|
```
|
|
|
|
The compiled binary is at `zig-out/bin/zfin`.
|
|
|
|
## Vendored code
|
|
|
|
A small amount of third-party source is vendored directly into the
|
|
tree (rather than added as a Zig package dependency) where the
|
|
upstream is small, stable, and not packaged for `build.zig.zon`:
|
|
|
|
| File | Source | Purpose |
|
|
|-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|
|
|
| `src/providers/xml.zig` | [Snektron/vulkan-zig](https://github.com/Snektron/vulkan-zig/blob/797ae8af88e84753af9640266de61a985b76b580/generator/xml.zig), via [aws-zig](https://github.com/elerch/aws-sdk-for-zig) | XML DOM parser used by the EDGAR provider for NPORT-P primary documents. |
|
|
|
|
Each vendored file carries a `// VENDORED - see README.md` header
|
|
identifying its upstream source. When updating, copy the new
|
|
upstream verbatim and re-add the header.
|
|
|
|
## License
|
|
|
|
MIT
|