diff --git a/README.md b/README.md index 2a6336d..eb6ba2e 100644 --- a/README.md +++ b/README.md @@ -33,15 +33,15 @@ zfin aggregates data from multiple free-tier APIs. Each provider is used for the ### Provider summary -| Data type | Provider | Auth | Free-tier limit | Cache TTL | -|---|---|---|---|---| -| Daily candles (OHLCV) | TwelveData | `TWELVEDATA_API_KEY` | 8 req/min, 800/day | 24 hours | -| Real-time quotes | TwelveData | `TWELVEDATA_API_KEY` | 8 req/min, 800/day | Never cached | -| Dividends | Polygon | `POLYGON_API_KEY` | 5 req/min | 7 days | -| Splits | Polygon | `POLYGON_API_KEY` | 5 req/min | 7 days | -| Options chains | CBOE | None required | ~30 req/min (self-imposed) | 1 hour | -| Earnings | Finnhub | `FINNHUB_API_KEY` | 60 req/min | 24 hours | -| ETF profiles | Alpha Vantage | `ALPHAVANTAGE_API_KEY` | 25 req/day | 30 days | +| Data type | Provider | Auth | Free-tier limit | Cache TTL | +|-----------------------|---------------|------------------------|----------------------------|--------------| +| Daily candles (OHLCV) | TwelveData | `TWELVEDATA_API_KEY` | 8 req/min, 800/day | 24 hours | +| Real-time quotes | TwelveData | `TWELVEDATA_API_KEY` | 8 req/min, 800/day | Never cached | +| Dividends | Polygon | `POLYGON_API_KEY` | 5 req/min | 7 days | +| Splits | Polygon | `POLYGON_API_KEY` | 5 req/min | 7 days | +| Options chains | CBOE | None required | ~30 req/min (self-imposed) | 1 hour | +| Earnings | Finnhub | `FINNHUB_API_KEY` | 60 req/min | 24 hours | +| ETF profiles | Alpha Vantage | `ALPHAVANTAGE_API_KEY` | 25 req/day | 30 days | ### TwelveData @@ -102,12 +102,12 @@ The cache directory defaults to `~/.cache/zfin` and can be overridden with `ZFIN Not all keys are required. Without a key, the corresponding data simply won't be available: -| Key | Without it | -|---|---| -| `TWELVEDATA_API_KEY` | No candles, quotes, or trailing returns | -| `POLYGON_API_KEY` | No dividends -- trailing returns show price-only (no total return) | -| `FINNHUB_API_KEY` | No earnings data (tab disabled) | -| `ALPHAVANTAGE_API_KEY` | No ETF profiles | +| Key | Without it | +|------------------------|--------------------------------------------------------------------| +| `TWELVEDATA_API_KEY` | No candles, quotes, or trailing returns | +| `POLYGON_API_KEY` | No dividends -- trailing returns show price-only (no total return) | +| `FINNHUB_API_KEY` | No earnings data (tab disabled) | +| `ALPHAVANTAGE_API_KEY` | No ETF profiles | CBOE options require no API key. @@ -121,15 +121,15 @@ Every data fetch follows the same pattern: Cache files use [SRF](https://github.com/lobo/srf) (Simple Record Format), a line-oriented key-value format. Freshness is determined by file modification time vs. the TTL for that data type. -| Data type | TTL | Rationale | -|---|---|---| -| Daily candles | 24 hours | Only changes once per trading day | -| Dividends | 7 days | Declared well in advance | -| Splits | 7 days | Rare corporate events | -| Options | 1 hour | Prices change continuously during market hours | -| Earnings | 24 hours | Quarterly events, estimates update periodically | -| ETF profiles | 30 days | Holdings/weights change slowly | -| Quotes | Never cached | Intended for live price checks | +| Data type | TTL | Rationale | +|---------------|--------------|-------------------------------------------------| +| Daily candles | 24 hours | Only changes once per trading day | +| Dividends | 7 days | Declared well in advance | +| Splits | 7 days | Rare corporate events | +| Options | 1 hour | Prices change continuously during market hours | +| Earnings | 24 hours | Quarterly events, estimates update periodically | +| ETF profiles | 30 days | Holdings/weights change slowly | +| Quotes | Never cached | Intended for live price checks | Manual refresh (`r` / `F5` in TUI) invalidates the cache for the current tab's data before re-fetching. @@ -137,13 +137,13 @@ 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: -| Provider | Rate limit | -|---|---| -| TwelveData | 8/minute | -| Polygon | 5/minute | -| Finnhub | 60/minute | -| CBOE | 30/minute | -| Alpha Vantage | 25/day | +| Provider | Rate limit | +|---------------|------------| +| TwelveData | 8/minute | +| Polygon | 5/minute | +| Finnhub | 60/minute | +| CBOE | 30/minute | +| Alpha Vantage | 25/day | The limiter blocks until a token is available, spreading bursts of requests automatically rather than failing with 429 errors. @@ -212,33 +212,33 @@ Default keybindings: | Key | Action | |---|---| -| `q`, `Ctrl+c` | Quit | -| `r`, `F5` | Refresh current tab (invalidates cache) | -| `R` | Reload portfolio from disk (no network) | -| `h`, Left | Previous tab | -| `l`, Right, Tab | Next tab | -| `1`-`6` | Jump to tab | -| `j`, Down | Select next row | -| `k`, Up | Select previous row | -| `Enter` | Expand/collapse (positions, expirations, calls/puts) | -| `s` | Select symbol from portfolio for other tabs | -| `/` | Enter symbol search | -| `e` | Edit portfolio/watchlist in `$EDITOR` | -| `<` | Sort by previous column (portfolio tab) | -| `>` | Sort by next column (portfolio tab) | -| `o` | Reverse sort direction (portfolio tab) | -| `[` | Previous chart timeframe (quote tab) | -| `]` | Next chart timeframe (quote tab) | -| `c` | Toggle all calls collapsed/expanded (options tab) | -| `p` | Toggle all puts collapsed/expanded (options tab) | -| `Ctrl+1`-`Ctrl+9` | Set options near-the-money filter to +/- N strikes | -| `g` | Scroll to top | -| `G` | Scroll to bottom | -| `Ctrl+d` | Half-page down | -| `Ctrl+u` | Half-page up | -| `PageDown` | Page down | -| `PageUp` | Page up | -| `?` | Help screen | +| `q`, `Ctrl+c` | Quit | +| `r`, `F5` | Refresh current tab (invalidates cache) | +| `R` | Reload portfolio from disk (no network) | +| `h`, Left | Previous tab | +| `l`, Right, Tab | Next tab | +| `1`-`6` | Jump to tab | +| `j`, Down | Select next row | +| `k`, Up | Select previous row | +| `Enter` | Expand/collapse (positions, expirations, calls/puts) | +| `s` | Select symbol from portfolio for other tabs | +| `/` | Enter symbol search | +| `e` | Edit portfolio/watchlist in `$EDITOR` | +| `<` | Sort by previous column (portfolio tab) | +| `>` | Sort by next column (portfolio tab) | +| `o` | Reverse sort direction (portfolio tab) | +| `[` | Previous chart timeframe (quote tab) | +| `]` | Next chart timeframe (quote tab) | +| `c` | Toggle all calls collapsed/expanded (options tab) | +| `p` | Toggle all puts collapsed/expanded (options tab) | +| `Ctrl+1`-`Ctrl+9` | Set options near-the-money filter to +/- N strikes | +| `g` | Scroll to top | +| `G` | Scroll to bottom | +| `Ctrl+d` | Half-page down | +| `Ctrl+u` | Half-page up | +| `PageDown` | Page down | +| `PageUp` | Page up | +| `?` | Help screen | Mouse: scroll wheel navigates, left-click selects rows and switches tabs, double-click expands/collapses. @@ -295,23 +295,23 @@ security_type::watch,symbol::TSLA ### Lot fields -| Field | Type | Required | Description | -|---|---|---|---| -| `symbol` | string | Yes* | Ticker symbol or CUSIP. *Optional for `cash` lots. | -| `shares` | number | Yes | Number of shares (or face value for cash/CDs) | -| `open_date` | string | Yes** | Purchase date (YYYY-MM-DD). **Not required for cash/watch. | -| `open_price` | number | Yes** | Purchase price per share. **Not required for cash/watch. | -| `close_date` | string | No | Sale date (null = open lot) | -| `close_price` | number | No | Sale price per share | -| `security_type` | string | No | `stock` (default), `option`, `cd`, `cash`, `illiquid`, `watch` | -| `account` | string | No | Account name (e.g. "Roth IRA", "Brokerage") | -| `note` | string | No | Descriptive note (shown in cash/CD/illiquid tables) | -| `ticker` | string | No | Ticker alias for price fetching (overrides `symbol` for API calls) | -| `price` | number | No | Manual price override (fallback when API has no coverage) | -| `price_date` | string | No | Date of the manual price (YYYY-MM-DD, for staleness display) | -| `drip` | string | No | `true` if lot is from dividend reinvestment (summarized as ST/LT groups) | -| `maturity_date` | string | No | CD maturity date (YYYY-MM-DD) | -| `rate` | number | No | Interest rate for CDs (e.g. 5.25 = 5.25%) | +| Field | Type | Required | Description | +|-----------------|--------|----------|--------------------------------------------------------------------------| +| `symbol` | string | Yes* | Ticker symbol or CUSIP. *Optional for `cash` lots. | +| `shares` | number | Yes | Number of shares (or face value for cash/CDs) | +| `open_date` | string | Yes** | Purchase date (YYYY-MM-DD). **Not required for cash/watch. | +| `open_price` | number | Yes** | Purchase price per share. **Not required for cash/watch. | +| `close_date` | string | No | Sale date (null = open lot) | +| `close_price` | number | No | Sale price per share | +| `security_type` | string | No | `stock` (default), `option`, `cd`, `cash`, `illiquid`, `watch` | +| `account` | string | No | Account name (e.g. "Roth IRA", "Brokerage") | +| `note` | string | No | Descriptive note (shown in cash/CD/illiquid tables) | +| `ticker` | string | No | Ticker alias for price fetching (overrides `symbol` for API calls) | +| `price` | number | No | Manual price override (fallback when API has no coverage) | +| `price_date` | string | No | Date of the manual price (YYYY-MM-DD, for staleness display) | +| `drip` | string | No | `true` if lot is from dividend reinvestment (summarized as ST/LT groups) | +| `maturity_date` | string | No | CD maturity date (YYYY-MM-DD) | +| `rate` | number | No | Interest rate for CDs (e.g. 5.25 = 5.25%) | ### Security types @@ -385,13 +385,13 @@ symbol::ARCC,sector::Financials,geo::US,asset_class::US Large Cap ### Classification fields -| Field | Type | Required | Description | -|---|---|---|---| -| `symbol` | string | Yes | Ticker symbol or CUSIP (must match `symbol::` or `ticker::` in portfolio) | -| `asset_class` | string | No | e.g. "US Large Cap", "Bonds", "Cash & CDs", "Emerging Markets" | -| `sector` | string | No | e.g. "Technology", "Healthcare", "Financials" | -| `geo` | string | No | e.g. "US", "International Developed", "Emerging Markets" | -| `pct` | number | No | Percentage weight for this entry (default 100). Use for blended funds. | +| Field | Type | Required | Description | +|---------------|--------|----------|---------------------------------------------------------------------------| +| `symbol` | string | Yes | Ticker symbol or CUSIP (must match `symbol::` or `ticker::` in portfolio) | +| `asset_class` | string | No | e.g. "US Large Cap", "Bonds", "Cash & CDs", "Emerging Markets" | +| `sector` | string | No | e.g. "Technology", "Healthcare", "Financials" | +| `geo` | string | No | e.g. "US", "International Developed", "Emerging Markets" | +| `pct` | number | No | Percentage weight for this entry (default 100). Use for blended funds. | For single-asset-class securities (individual stocks, single-focus ETFs), one line at the default 100% is sufficient. For multi-asset-class funds (target date, balanced), add multiple lines for the same symbol with `pct:num:` values that sum to approximately 100. diff --git a/src/tui/keybinds.zig b/src/tui/keybinds.zig index 709ad2b..c81d4cc 100644 --- a/src/tui/keybinds.zig +++ b/src/tui/keybinds.zig @@ -84,6 +84,7 @@ const default_bindings = [_]Binding{ .{ .action = .refresh, .key = .{ .codepoint = vaxis.Key.f5 } }, .{ .action = .prev_tab, .key = .{ .codepoint = 'h' } }, .{ .action = .prev_tab, .key = .{ .codepoint = vaxis.Key.left } }, + .{ .action = .prev_tab, .key = .{ .codepoint = vaxis.Key.tab, .mods = .{ .shift = true } } }, .{ .action = .next_tab, .key = .{ .codepoint = 'l' } }, .{ .action = .next_tab, .key = .{ .codepoint = vaxis.Key.right } }, .{ .action = .next_tab, .key = .{ .codepoint = vaxis.Key.tab } },