zfin-server/README.md
Emil Lerch 0897eb850f
All checks were successful
Generic zig build / build (push) Successful in 3m39s
Generic zig build / deploy (push) Successful in 16s
add edgar/wikidata endpoints/refresh, zfin upgrade
2026-06-01 08:12:51 -07:00

106 lines
3.6 KiB
Markdown

# zfin-server
HTTP data service backed by [zfin](https://git.lerch.org/lobo/zfin)'s financial
data providers. Serves cached market data over HTTP for two consumers:
1. **LibreCalc spreadsheets** — trailing returns via `WEBSERVICE`/`FILTERXML`
2. **zfin CLI/TUI** — raw SRF cache files as an L1 data cache
## Quick start
```sh
# Set API keys (same as zfin)
export TWELVEDATA_API_KEY=...
export POLYGON_API_KEY=...
# Start the server
zig build run -- serve --port=8080
# In another terminal
curl http://localhost:8080/AAPL/returns
curl http://localhost:8080/AAPL/returns?fmt=xml
```
## Endpoints
| Route | Content-Type | Description |
|-------|-------------|-------------|
| `GET /` | `text/html` | Landing page |
| `GET /help` | `text/plain` | Endpoint documentation |
| `GET /symbols` | `application/json` | List of tracked symbols |
| `GET /:symbol/returns` | `application/json` | Trailing 1/3/5/10yr returns + volatility |
| `GET /:symbol/returns?fmt=xml` | `application/xml` | Same, XML for LibreCalc |
| `GET /:symbol/quote` | `application/json` | Latest quote |
| `GET /:symbol/candles` | `application/x-srf` | Raw SRF cache file |
| `GET /:symbol/dividends` | `application/x-srf` | Raw SRF cache file |
| `GET /:symbol/earnings` | `application/x-srf` | Raw SRF cache file |
| `GET /:symbol/options` | `application/x-srf` | Raw SRF cache file |
| `GET /:symbol/classification` | `application/x-srf` | Wikidata classification (SRF) |
| `GET /:symbol/etf_metrics` | `application/x-srf` | EDGAR NPORT-P fund metrics; 404 for non-funds |
| `GET /:cik/entity_facts` | `application/x-srf` | EDGAR XBRL entity facts (CIK-keyed) |
| `GET /_edgar/tickers_funds` | `application/x-srf` | EDGAR mutual-fund ticker map (~3 MB) |
| `GET /_edgar/tickers_companies` | `application/x-srf` | EDGAR company ticker map (~5 MB) |
## LibreCalc usage
```
=FILTERXML(WEBSERVICE("http://localhost:8080/AAPL/returns?fmt=xml"), "//trailing1YearReturn")
```
XML response format:
```xml
<returns>
<ticker>AAPL</ticker>
<returnDate>2026-03-07</returnDate>
<trailing1YearReturn>12.34000</trailing1YearReturn>
<trailing3YearReturn>8.56000</trailing3YearReturn>
<trailing5YearReturn>15.78000</trailing5YearReturn>
<trailing10YearReturn>22.10000</trailing10YearReturn>
<volatility>18.50000</volatility>
</returns>
```
## Cache refresh
Use cron to keep the cache warm for all symbols in your portfolio:
```sh
# Refresh all portfolio symbols daily at 5pm ET (after market close)
0 17 * * 1-5 cd /path/to/workdir && ZFIN_PORTFOLIO=portfolio.srf zfin-server refresh
```
The candle cache TTL is 23h45m (not a full 24h), providing a 15-minute jitter
buffer so daily cron runs at the same time always see stale data and trigger a
fresh fetch. Dividend and earnings TTLs are 14 and 30 days respectively.
The `refresh` command reads the portfolio SRF file (defaults to `portfolio.srf`
in the current directory if `ZFIN_PORTFOLIO` is not set), extracts all stock and
watch symbols, and fetches candles, dividends, and earnings for each.
## Building
Requires [Zig 0.16.0](https://ziglang.org/download/).
```sh
zig build # build
zig build test # run tests
zig build run -- serve --port=8080
```
## Configuration
All configuration is via environment variables:
| Variable | Required | Description |
|----------|----------|-------------|
| `TWELVEDATA_API_KEY` | Yes | TwelveData API key |
| `POLYGON_API_KEY` | No | Polygon API key |
| `FINNHUB_API_KEY` | No | Finnhub API key |
| `ZFIN_USER_EMAIL` | Yes | Contact email for SEC EDGAR User-Agent header |
| `ZFIN_PORTFOLIO` | No | Path to portfolio SRF (default: `portfolio.srf`) |
| `ZFIN_CACHE_DIR` | No | Cache directory (default: `~/.cache/zfin`) |
## License
MIT