Skip to main content

ETF research and portfolio analytics terminal

Project description

etfray

CI Docs PyPI License: MIT

etfray

A terminal-based ETF research and portfolio analytics application built with Textual.

etfray converts SEC fund filings and IBKR portfolio data into holdings, exposure, concentration, margin, and risk workflows — all from your terminal.

Why etfray?

  • No cloud accounts — No sign-ups, no API keys to manage, no third-party dashboards. Your data stays on your machine.
  • No subscriptions — ETF holdings data comes directly from SEC EDGAR filings. Free, authoritative, and always available.
  • Keyboard-first — Designed for speed. Command palette, tree navigation, and keybindings — no mouse required.

Features

  • ETF Research — Search ETFs, view holdings, sector/geographic exposure, concentration, fees, risk metrics, and SEC documents via EDGAR
  • Seasonals — TradingView-style seasonals chart with year-over-year cumulative returns, period returns table (1W to Max), and year range selection
  • Fund Overview — Rich fund profile combining SEC filings with Yahoo Finance metadata (category, expense ratio, dividend yield, beta, returns, description)
  • Watchlist — Track ETFs with at-a-glance metrics: concentration, top sectors, overlap vs portfolio, and data freshness
  • Portfolio Analytics — Connect to IBKR TWS/Gateway for live positions, lookthrough exposure, concentration analysis, and margin/leverage monitoring
  • Side-by-side Compare — Compare multiple ETFs across holdings, exposure, and fees in a single view
  • Export — Save any view to CSV or JSON for further analysis
  • Keyboard-first — Full TUI with command palette, tree navigation, and keybindings
  • Local & private — All data cached locally in SQLite; no cloud accounts required

Holdings view

Seasonals view

Side-by-side Compare

Portfolio ETF Lookthrough

Key Capabilities

Capability Details
ETF coverage Thousands of ETFs via SEC EDGAR N-PORT filings
Data sources EDGAR (official), alternative web scraper, Yahoo Finance (metadata & price history), IBKR TWS
Holdings analysis Full position-level breakdown with weight, value, shares
Fund metadata Category, expense ratio, dividend yield, beta, inception date, returns via yfinance
Seasonals Year-over-year cumulative return chart with matplotlib or plotext rendering
Exposure Sector and geographic exposure from underlying holdings
Concentration Top-N analysis (top 10, 25, 50) with cumulative weight
Watchlist Track ETFs with concentration metrics, sector breakdown, and portfolio overlap
Portfolio Real-time positions, lookthrough exposure, margin & leverage
Storage Local SQLite — no cloud, no external databases
Freshness Configurable staleness thresholds (default: 30 days fresh, 90 days acceptable)

Usage Examples

Research an ETF

  1. Launch etfray and navigate to Research → Search in the sidebar
  2. Press / to open ETF Search, type a ticker (e.g., VTI), and press Enter
  3. Browse tabs: OverviewSeasonalsHoldingsExposureConcentrationRisk
  4. Press w to add the ETF to your watchlist

View seasonals

  1. Search for an ETF (e.g., SPY)
  2. Press t to jump to the Seasonals view
  3. Select year range to compare seasonal patterns across years
  4. Review the period returns table for standard return intervals

Manage your watchlist

  1. Navigate to Workspace → Watchlist in the sidebar
  2. Click Add ticker to search and add ETFs
  3. View concentration, sector, and overlap metrics at a glance
  4. Double-click any row to open that ETF's research view

Monitor your portfolio

  1. Ensure IBKR TWS/Gateway is running with API enabled on port 7497
  2. Navigate to Portfolio → Positions in the sidebar
  3. etfray connects lazily — positions load automatically on first access
  4. Switch to Lookthrough to see aggregated exposure across all your ETF holdings
  5. Check Margin for leverage ratio and margin cushion warnings

Architecture

graph LR
    A[SEC EDGAR API] --> C[Data Services]
    B[Web Scraper] --> C
    Y[Yahoo Finance] --> C
    D[IBKR TWS API] --> C
    C --> E[(SQLite Cache)]
    E --> F[Domain Analytics]
    F --> G[Textual TUI]

Design principles:

  • Local-first — All data cached in SQLite. Works offline after initial fetch.
  • Source provenance — Every data point tracks its origin and fetch date so you know how fresh it is.
  • Lazy connection — IBKR connects only when portfolio views are accessed, not at startup.
  • Separation of concernsdata/ handles I/O, domain/ handles computation, ui/ handles presentation.

Configuration

All settings are managed via Workspace → Settings in the sidebar and stored in ~/.etfray/data.db.

Setting Default Description
ibkr_port 7497 IBKR TWS/Gateway API port
edgar_identity (empty) Your email — required by SEC fair use policy
data_source auto Holdings source: auto, edgar, or web
freshness_days_fresh 30 Days before cached data is no longer considered fresh
margin_warning_cushion 0.15 Margin cushion threshold for warnings

See the full configuration reference for all options.

Installation

pip install etfray

Requires Python 3.11+.

Seasonals chart (optional): For a matplotlib seasonals chart in the Seasonals tab:

pip install etfray[charts]
# or from source:
pip install -e ".[charts]"

Verify dependencies: python scripts/check_charts.py (should report Chart: image (matplotlib) and True).

Terminal image support is required for a crisp chart (not blocky ASCII). Enable one of:

  • Cursor / VS Code: Settings → terminal.integrated.enableImagestrue, then restart the terminal
  • iTerm2, Kitty, WezTerm, or Windows Terminal 1.22+ (recommended)

Without [charts] or without image support, etfray uses an ASCII plotext chart and shows the active mode in the Seasonals summary line.

Blurry chart? If the summary says Chart: image (halfcell) or (unicode), the terminal is using a low-resolution block renderer. For a sharp chart, run etfray in iTerm2 or Kitty, or enable Cursor terminal.integrated.enableImages and restart the terminal. Check python scripts/check_charts.py for protocol: sixel or tgp.

Quick Start

etfray

Use the sidebar tree to navigate between Research and Portfolio workspaces. Press ctrl+p to open the command palette.

IBKR Connection

To use portfolio analytics, you need IBKR TWS or IB Gateway running with API connections enabled (default port 7497).

Configure the connection in Workspace → Settings in the sidebar.

Documentation

Full documentation at etfray.readthedocs.io:

Development

git clone https://github.com/alwank/etfray.git
cd etfray
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev,docs]"
pytest

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

etfray-0.2.1.tar.gz (31.0 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

etfray-0.2.1-py3-none-any.whl (84.0 kB view details)

Uploaded Python 3

File details

Details for the file etfray-0.2.1.tar.gz.

File metadata

  • Download URL: etfray-0.2.1.tar.gz
  • Upload date:
  • Size: 31.0 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for etfray-0.2.1.tar.gz
Algorithm Hash digest
SHA256 a41ac6c897eeccbe4a13e7f40dc334de21ac29b879c158cd0ba1bc71576d155d
MD5 5a3d7b0a22b905879fb4ec2a26f632e7
BLAKE2b-256 f96888aa19a2672b549f60311f40dab1d5632c72f5e6b4b244469e55521ca9f9

See more details on using hashes here.

Provenance

The following attestation bundles were made for etfray-0.2.1.tar.gz:

Publisher: publish.yml on alwank/etfray

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file etfray-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: etfray-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 84.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for etfray-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d8b8a37715b35dd34f15cef99df0afa5e3bde8d7d4493e8b21339fbd2afc1f89
MD5 6c508e7aa60a22169fb085b5fe58ca99
BLAKE2b-256 b33e8fab52e08571d57c176cde77b8d2ee907d260fb82ac5171abcab9a6f8573

See more details on using hashes here.

Provenance

The following attestation bundles were made for etfray-0.2.1-py3-none-any.whl:

Publisher: publish.yml on alwank/etfray

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page