README update
This commit is contained in:
parent
195933176f
commit
641a88b0b7
1 changed files with 118 additions and 60 deletions
178
README.md
178
README.md
|
|
@ -4,28 +4,52 @@ A financial data library, CLI, and terminal UI written in Zig. Tracks portfolios
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
|
### macOS (Apple Silicon) -- pre-built binary
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Set at least one API key (see "API keys" below)
|
# Download the latest aarch64-macos binary, make it executable, drop on $PATH
|
||||||
export TIINGO_API_KEY=your_key
|
curl -L -o zfin \
|
||||||
|
https://git.lerch.org/api/packages/lobo/generic/zfin-aarch64-macos/latest/zfin-aarch64-macos
|
||||||
# Build
|
chmod +x zfin
|
||||||
zig build
|
sudo mv zfin /usr/local/bin/ # or anywhere on $PATH
|
||||||
|
|
||||||
# CLI usage
|
|
||||||
zig build run -- perf VTI # trailing returns
|
|
||||||
zig build run -- quote AAPL # real-time quote
|
|
||||||
zig build run -- options AAPL # options chains
|
|
||||||
zig build run -- earnings MSFT # earnings history
|
|
||||||
zig build run -- portfolio # portfolio summary (reads portfolio.srf)
|
|
||||||
zig build run -- analysis # portfolio analysis (reads portfolio.srf + metadata.srf)
|
|
||||||
|
|
||||||
# Interactive TUI
|
|
||||||
zig build run -- i # auto-loads portfolio.srf from cwd
|
|
||||||
zig build run -- i -p portfolio.srf -w watchlist.srf
|
|
||||||
zig build run -- i -s AAPL # start with a symbol, no portfolio
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Requires Zig 0.15.2 or later.
|
### 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.
|
||||||
|
|
||||||
## Data providers
|
## Data providers
|
||||||
|
|
||||||
|
|
@ -33,15 +57,17 @@ zfin aggregates data from multiple free-tier APIs. Each provider is used for the
|
||||||
|
|
||||||
### Provider summary - primary providers
|
### Provider summary - primary providers
|
||||||
|
|
||||||
| Data type | Provider | Auth | Free-tier limit | Cache TTL |
|
| Data type | Provider | Auth | Free-tier limit | Cache TTL |
|
||||||
|-----------------------|---------------|------------------------|----------------------------|--------------|
|
|-----------------------|------------------|----------------------|----------------------------|--------------|
|
||||||
| Daily candles (OHLCV) | Tiingo | `TIINGO_API_KEY` | 1,000 req/day | 23h45m |
|
| Daily candles (OHLCV) | Tiingo | `TIINGO_API_KEY` | 1,000 req/day | 23h45m |
|
||||||
| Real-time quotes | Yahoo Finance | None required | Unofficial | Never cached |
|
| Real-time quotes | Yahoo Finance | None required | Unofficial | Never cached |
|
||||||
| Dividends | Polygon | `POLYGON_API_KEY` | 5 req/min | 14 days |
|
| Quote fallback | TwelveData | `TWELVEDATA_API_KEY` | 8 req/min, 800/day | Never cached |
|
||||||
| Splits | Polygon | `POLYGON_API_KEY` | 5 req/min | 14 days |
|
| Dividends | Polygon | `POLYGON_API_KEY` | 5 req/min | 14 days |
|
||||||
| Options chains | CBOE | None required | ~30 req/min (self-imposed) | 1 hour |
|
| Splits | Polygon | `POLYGON_API_KEY` | 5 req/min | 14 days |
|
||||||
| Earnings | FMP | `FMP_API_KEY` | 250 req/day | 30 days* |
|
| Options chains | CBOE | None required | ~30 req/min (self-imposed) | 1 hour |
|
||||||
| ETF profiles | Alpha Vantage | `ALPHAVANTAGE_API_KEY` | 25 req/day | 30 days |
|
| 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 |
|
||||||
|
|
||||||
### Tiingo
|
### Tiingo
|
||||||
|
|
||||||
|
|
@ -89,36 +115,51 @@ zfin aggregates data from multiple free-tier APIs. Each provider is used for the
|
||||||
- History depth: full — often back to the 1980s for long-listed tickers.
|
- History depth: full — often back to the 1980s for long-listed tickers.
|
||||||
- Coverage: US stocks with real earnings. ETFs, mutual funds, CUSIPs, and some dual-class shares (BRK.B, GOOG) return 402 on the free tier and show up as "no earnings data" in the UI — a documented limitation, not a bug.
|
- Coverage: US stocks with real earnings. ETFs, mutual funds, CUSIPs, and some dual-class shares (BRK.B, GOOG) return 402 on the free tier and show up as "no earnings data" in the UI — a documented limitation, not a bug.
|
||||||
|
|
||||||
### Alpha Vantage
|
### SEC EDGAR + Wikidata
|
||||||
|
|
||||||
**Used for:** ETF profiles (expense ratio, holdings, sector weights).
|
**Used for:** ETF profiles (NPORT-P holdings, sector weights, AUM, inception
|
||||||
|
dates) and the `enrich` flow that bootstraps `metadata.srf` (sector / industry
|
||||||
|
/ country / asset-class classification).
|
||||||
|
|
||||||
- Endpoint: `https://www.alphavantage.co/query?function=ETF_PROFILE`
|
- Endpoints:
|
||||||
- Free tier: 25 requests per day. Used sparingly -- ETF profiles rarely change.
|
- `https://data.sec.gov/...` -- XBRL company facts, NPORT-P primary
|
||||||
|
documents, mutual-fund ticker map.
|
||||||
|
- `https://www.wikidata.org/sparql` -- sector / industry / country
|
||||||
|
statements.
|
||||||
|
- Free, but the SEC requires a contact email in the User-Agent header. zfin
|
||||||
|
reads this from `ZFIN_USER_EMAIL` (`.env` or environment). Without it, ETF
|
||||||
|
profiles and `enrich` are unavailable; everything else still works.
|
||||||
|
- The SEC caps requests at 10/sec; the rate limiter respects this. Wikidata
|
||||||
|
has no per-day quota.
|
||||||
|
- The `enrich` command queries Wikidata first (rich classification metadata);
|
||||||
|
when Wikidata has no entry for a symbol (common for managed funds, UITs),
|
||||||
|
EDGAR's mutual-fund ticker map is the fallback. Symbols that miss both fall
|
||||||
|
through as TODO entries the user fills in by hand.
|
||||||
|
|
||||||
## API keys
|
## API keys
|
||||||
|
|
||||||
Set keys as environment variables or in a `.env` file (searched in the executable's parent directory, then cwd):
|
Set keys as environment variables or in a `.env` file (searched in the executable's parent directory, then cwd):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
TIINGO_API_KEY=your_key # Required for candles (primary provider)
|
TIINGO_API_KEY=your_key # Required for candles (primary provider)
|
||||||
TWELVEDATA_API_KEY=your_key # Quote fallback (after Yahoo)
|
TWELVEDATA_API_KEY=your_key # Quote fallback (after Yahoo)
|
||||||
POLYGON_API_KEY=your_key # Required for dividends/splits (total returns)
|
POLYGON_API_KEY=your_key # Required for dividends/splits (total returns)
|
||||||
FMP_API_KEY=your_key # Required for earnings data
|
FMP_API_KEY=your_key # Required for earnings data
|
||||||
ALPHAVANTAGE_API_KEY=your_key # Required for ETF profiles
|
ZFIN_USER_EMAIL=you@example.com # Required for ETF profiles + `enrich`
|
||||||
|
# (SEC EDGAR mandates a User-Agent contact)
|
||||||
```
|
```
|
||||||
|
|
||||||
The cache directory defaults to `~/.cache/zfin` and can be overridden with `ZFIN_CACHE_DIR`.
|
The cache directory defaults to `~/.cache/zfin` and can be overridden with `ZFIN_CACHE_DIR`.
|
||||||
|
|
||||||
Not all keys are required. Without a key, the corresponding data simply won't be available:
|
Not all keys are required. Without a key, the corresponding data simply won't be available:
|
||||||
|
|
||||||
| Key | Without it |
|
| Key | Without it |
|
||||||
|------------------------|-------------------------------------------------------------------------------------|
|
|----------------------|-------------------------------------------------------------------------------------------|
|
||||||
| `TIINGO_API_KEY` | Candles fall back to Yahoo only; some symbols (especially mutual funds) won't work |
|
| `TIINGO_API_KEY` | Candles fall back to Yahoo only; some symbols (especially mutual funds) won't work |
|
||||||
| `TWELVEDATA_API_KEY` | No quote fallback after Yahoo |
|
| `TWELVEDATA_API_KEY` | No quote fallback after Yahoo |
|
||||||
| `POLYGON_API_KEY` | No forward-looking dividends; trailing total returns may use only Tiingo's view |
|
| `POLYGON_API_KEY` | No forward-looking dividends; trailing total returns may use only Tiingo's view |
|
||||||
| `FMP_API_KEY` | No earnings data (tab disabled) |
|
| `FMP_API_KEY` | No earnings data (tab disabled) |
|
||||||
| `ALPHAVANTAGE_API_KEY` | No ETF profiles |
|
| `ZFIN_USER_EMAIL` | No ETF profiles; `enrich` cannot bootstrap metadata. (Used as the EDGAR User-Agent value) |
|
||||||
|
|
||||||
CBOE options require no API key.
|
CBOE options require no API key.
|
||||||
|
|
||||||
|
|
@ -150,14 +191,15 @@ Manual refresh (`r` / `F5` in TUI) invalidates the cache for the current tab's d
|
||||||
|
|
||||||
Each provider has a client-side token-bucket rate limiter that prevents exceeding free-tier limits:
|
Each provider has a client-side token-bucket rate limiter that prevents exceeding free-tier limits:
|
||||||
|
|
||||||
| Provider | Rate limit |
|
| Provider | Rate limit |
|
||||||
|---------------|-------------|
|
|---------------|---------------------|
|
||||||
| Tiingo | 1,000/day |
|
| Tiingo | 1,000/day |
|
||||||
| TwelveData | 8/minute |
|
| TwelveData | 8/minute |
|
||||||
| Polygon | 5/minute |
|
| Polygon | 5/minute |
|
||||||
| FMP | 250/day |
|
| FMP | 250/day |
|
||||||
| CBOE | 30/minute |
|
| CBOE | 30/minute |
|
||||||
| Alpha Vantage | 25/day |
|
| SEC EDGAR | 10/second |
|
||||||
|
| Wikidata | (no enforced limit) |
|
||||||
|
|
||||||
The limiter blocks until a token is available, spreading bursts of requests automatically rather than failing with 429 errors.
|
The limiter blocks until a token is available, spreading bursts of requests automatically rather than failing with 429 errors.
|
||||||
|
|
||||||
|
|
@ -500,7 +542,9 @@ Cash and CD lots are automatically classified as "Cash & CDs" without needing me
|
||||||
|
|
||||||
### Bootstrapping metadata
|
### Bootstrapping metadata
|
||||||
|
|
||||||
Use the `enrich` command to generate a starting `metadata.srf` from Alpha Vantage company overview data:
|
Use the `enrich` command to generate a starting `metadata.srf` from Wikidata
|
||||||
|
+ SEC EDGAR data. Requires `ZFIN_USER_EMAIL` set so EDGAR will accept the
|
||||||
|
request.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Enrich an entire portfolio (generates full metadata.srf)
|
# Enrich an entire portfolio (generates full metadata.srf)
|
||||||
|
|
@ -510,7 +554,12 @@ zfin enrich portfolio.srf > metadata.srf
|
||||||
zfin enrich SCHD >> metadata.srf
|
zfin enrich SCHD >> metadata.srf
|
||||||
```
|
```
|
||||||
|
|
||||||
When given a file path, it fetches all stock symbols and outputs a complete SRF file with headers. When given a symbol, it outputs just the classification lines (no header), so you can append directly with `>>`.
|
When given a file path, it fetches all stock symbols and outputs a complete
|
||||||
|
SRF file with headers. When given a symbol, it outputs just the
|
||||||
|
classification lines (no header), so you can append directly with `>>`.
|
||||||
|
Wikidata is queried first; symbols not found in Wikidata fall through to
|
||||||
|
EDGAR's mutual-fund ticker map. Anything that misses both shows up as a
|
||||||
|
TODO entry to fill in by hand.
|
||||||
|
|
||||||
## Account metadata (accounts.srf)
|
## Account metadata (accounts.srf)
|
||||||
|
|
||||||
|
|
@ -832,7 +881,7 @@ Commands:
|
||||||
analysis [FILE] Portfolio analysis breakdowns (default: portfolio.srf)
|
analysis [FILE] Portfolio analysis breakdowns (default: portfolio.srf)
|
||||||
snapshot [opts] Write a daily portfolio snapshot to history/
|
snapshot [opts] Write a daily portfolio snapshot to history/
|
||||||
compare <D1> [<D2>] Compare portfolio state across two dates
|
compare <D1> [<D2>] Compare portfolio state across two dates
|
||||||
enrich <FILE|SYMBOL> Generate metadata.srf from Alpha Vantage
|
enrich <FILE|SYMBOL> Generate metadata.srf from Wikidata + SEC EDGAR
|
||||||
lookup <CUSIP> CUSIP to ticker lookup via OpenFIGI
|
lookup <CUSIP> CUSIP to ticker lookup via OpenFIGI
|
||||||
cache stats Show cached symbols
|
cache stats Show cached symbols
|
||||||
cache clear Delete all cached data
|
cache clear Delete all cached data
|
||||||
|
|
@ -872,9 +921,11 @@ src/
|
||||||
polygon.zig Polygon: dividends, splits (primary, with forward-looking entries)
|
polygon.zig Polygon: dividends, splits (primary, with forward-looking entries)
|
||||||
fmp.zig FMP: earnings (actuals + estimates)
|
fmp.zig FMP: earnings (actuals + estimates)
|
||||||
cboe.zig CBOE: options chains (no API key)
|
cboe.zig CBOE: options chains (no API key)
|
||||||
alphavantage.zig Alpha Vantage: ETF profiles, company overview
|
Edgar.zig SEC EDGAR: ETF profiles (NPORT-P), mutual-fund ticker map, XBRL company facts
|
||||||
|
Wikidata.zig Wikidata SPARQL: classification metadata for `enrich`
|
||||||
yahoo.zig Yahoo Finance: quotes (primary), candles (Tiingo fallback)
|
yahoo.zig Yahoo Finance: quotes (primary), candles (Tiingo fallback)
|
||||||
openfigi.zig OpenFIGI: CUSIP to ticker lookup
|
openfigi.zig OpenFIGI: CUSIP to ticker lookup
|
||||||
|
xml.zig Vendored XML parser used by Edgar.zig (see "Vendored code")
|
||||||
analytics/
|
analytics/
|
||||||
indicators.zig SMA, Bollinger Bands, RSI
|
indicators.zig SMA, Bollinger Bands, RSI
|
||||||
performance.zig Trailing returns (as-of-date + month-end)
|
performance.zig Trailing returns (as-of-date + month-end)
|
||||||
|
|
@ -907,14 +958,21 @@ accounts.srf Account to tax type mapping for analysis
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
| Dependency | Source | Purpose |
|
| Dependency | Source | Purpose |
|
||||||
|----------------------------------------------------|---------------------|---------------------------------------------------|
|
|----------------------------------------------------|----------------|---------------------------------------------------|
|
||||||
| [SRF](https://git.lerch.org/lobo/srf) | Git | Cache file format and portfolio/watchlist parsing |
|
| [SRF](https://git.lerch.org/lobo/srf) | Git | Cache file format and portfolio/watchlist parsing |
|
||||||
| [libvaxis](https://github.com/rockorager/libvaxis) | Git (v0.5.1) | Terminal UI rendering |
|
| [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) |
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
Requires Zig 0.16.0. The canonical version is pinned in `.mise.toml`:
|
||||||
|
|
||||||
```bash
|
```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 # build the zfin binary
|
||||||
zig build test # run all tests
|
zig build test # run all tests
|
||||||
zig build run -- <args> # build and run
|
zig build run -- <args> # build and run
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue