# 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 | ## LibreCalc usage ``` =FILTERXML(WEBSERVICE("http://localhost:8080/AAPL/returns?fmt=xml"), "//trailing1YearReturn") ``` XML response format: ```xml AAPL 2026-03-07 12.34000 8.56000 15.78000 22.10000 18.50000 ``` ## 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.15.2](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 | | `ALPHAVANTAGE_API_KEY` | No | Alpha Vantage API key | | `ZFIN_PORTFOLIO` | No | Path to portfolio SRF (default: `portfolio.srf`) | | `ZFIN_CACHE_DIR` | No | Cache directory (default: `~/.cache/zfin`) | ## License MIT