Skip to main content

Fullscreen TUI stock screener inspired by Benjamin Graham

Project description

image

👤 Who Is Benjamin Graham?

Benjamin Graham (1894-1976) is widely considered the father of value investing. He promoted disciplined stock selection based on financial strength, earnings quality, and buying at a discount to intrinsic value.

This project applies a practical version of Graham-style screening in a fullscreen terminal UI.

Classical Graham-style rules:

  1. Adequate company size
    • Avoid very small companies with fragile access to financing and limited reporting quality.
    • In practice, this is often implemented with minimum revenue or market-cap thresholds.
  2. Strong financial condition
    • Balance-sheet resilience is central: healthy liquidity and controlled leverage.
    • Typical checks include current ratio, debt versus current assets, and debt service capacity.
  3. Earnings stability
    • Prefer businesses with positive earnings over a long period, avoiding repeated deficits.
    • Stability reduces downside risk and improves confidence in valuation inputs.
  4. Long dividend record
    • Graham historically favored companies paying regular dividends for many years (often ~20 years).
    • A long dividend history acts as a discipline signal for management and cash generation.
  5. Earnings growth
    • Look for sustained, not one-off, profit growth over multi-year windows.
    • CAGR-like approaches are commonly used to smooth noisy year-to-year moves.
  6. Moderate price/earnings ratio
    • Classical references often mention a cap around 15x earnings.
    • The spirit is paying a reasonable multiple, not maximum growth premiums.
  7. Moderate price/book ratio
    • Classical references often mention a cap near 1.5x book value.
    • Combined with P/E, this aims to avoid overpaying for low-quality balance sheets.

Reference:

⚠️ Disclaimer

  • Past performance does not guarantee future results.
  • Investing involves risk.
  • This application does not provide investment advice.
  • Over 20 years, among professional equity investors, more than 90% of funds underperform the market (SPIVA). Stock picking must therefore be approached with great caution.

🎬 Demo

https://github.com/user-attachments/assets/13609476-83ff-4abb-b83a-05ceda12e6ac

🚀 Installation

🧰 Prerequisites

  • Python 3.11+
  • Internet access (for market data providers and optional LLM calls)

🐧 Ubuntu

sudo apt install pipx
pipx ensurepath
pipx install .
graham

🍎 macOS

brew install pipx
pipx ensurepath
pipx install .
graham

🪟 Windows

py -m pip install --user pipx
py -m pipx ensurepath
pipx install .
graham

🔁 Alternative (pip)

python -m pip install .
graham

💻 Usage

  • graham launches the fullscreen TUI.
  • graham --help shows a minimal CLI help.

The app uses one input box at the bottom:

  • slash commands (/help, /scan, ...)
  • free prompt mode (if a ticker is selected, it behaves like /explain <ticker> "...")

Market data provider (optional):

  • default: yfinance
  • alternative: defeatbeta-api with GRAHAM_MARKET_DATA_PROVIDER=defeatbeta
  • if defeatbeta-api is unavailable or incomplete, the app falls back to yfinance automatically

✅ Features

  • Stock screener with ranking, details, and live output log.
  • Graham-style scoring with intrinsic value (V) and margin of safety (MoS).
  • Dynamic universes and index loaders (/universe, /indices) with persisted defaults.
  • Real-time price refresh with sortable table (score, price, as_of, MoS, P/E, ...).
  • Rich command UX: autocompletion, prompt history, keyboard shortcuts, quick ticker/company search.
  • Economic moat analysis via /moat TICKER with model-aware prompts and Markdown-rendered output.
  • Optional LLM workflows for /explain and /moat with deterministic fallback when unavailable.
  • Multi-language display support (/lang) persisted in ~/.graham/config.json.
  • Export scan results to csv or json.

📊 Product Flow

Pipeline:

  1. Load a universe from universes/*.txt
  2. Compute fundamentals once
  3. Refresh prices every X seconds
  4. Recompute Margin of Safety (MoS)
  5. Rank by:
    • score descending
    • MoS descending
    • P/E ascending

Ranking columns:

  • rank | ticker | company | score | rating | price | as_of | V | MoS | P/E | P/B | dividend

Score formula:

  • PASS / scored_criteria
  • N/A criteria are excluded from the denominator

🧠 Graham Logic

7 implemented criteria:

  1. S&P earnings/dividend rating >= B
    • Not available in yfinance -> N/A
    • Ignored in score if N/A
  2. Total debt / current assets < 1.10
  3. Current ratio > 1.50
  4. Positive EPS growth over ~5 years with no deficit (best effort if data is partial)
  5. P/E <= 9.0
  6. P/B < 1.20
  7. Dividends required by default (dividendRate > 0)

This app intentionally uses stricter default thresholds for criteria 5 and 6 (P/E <= 9.0, P/B < 1.20) to stay conservative.

Intrinsic value formula:

  • V = EPS * (8.5 + 2g) * 4.4 / Y
  • Y configurable (default 4.4)
  • g = EPS CAGR if available, otherwise 0
  • MoS = (V - price) / price

Important: the Graham formula should be used with caution in modern markets. Accounting standards, sector composition, intangible assets, and interest-rate regimes have changed significantly since the original framework.

Robustness policy:

  • Missing data => show N/A with an explanatory note.
  • Never crash by design (errors are captured and logged when possible).

🧭 Slash Commands

  • /help
  • /keys
  • /universes
  • /indices [name] (examples: sp500, msci_world, msci_emerging, dax40, nikkei225, csi300)
  • /languages
  • /lang [language-code]
  • /model [none|model-name]
  • /universe [sample|world|usa|emerging_markets|china|india|germany|europe|france|japan|custom:path]
  • /default-universe [name|custom:path]
  • /scan [--top N] [--min-score N] [--refresh SECONDS]
  • /screen TICKERS_CSV
  • /explain [TICKER] [optional question]
  • /moat TICKER
  • /rating GREEN ORANGE
  • /export [csv|json]

✨ Autocompletion

Context-aware autocompletion in the input overlay:

  • / -> command list
  • /lang -> common language codes (en, fr, es, de, ...)
  • /model -> none + model examples
  • /universe -> available universes + custom:path
  • /default-universe -> available presets
  • /export -> csv/json
  • /scan -> --top, --min-score, --refresh
  • /moat -> current universe tickers

Keyboard:

  • F1 show keyboard shortcuts
  • move in suggestions (or prompt history when no suggestion list is visible)
  • TAB complete
  • ENTER accept
  • Ctrl+L clear output log panel
  • Ctrl+R search/filter by ticker or company in the top ranking table

Mouse:

  • click a block in the output log to copy it
  • click the details/criteria panel to copy the full details block
  • on Linux, install wl-copy (Wayland) or xclip/xsel for reliable Ctrl+V paste

🤖 Optional LLM

Default model: none

  • none means no LLM API call
  • if a model is set, /explain and /moat can call litellm
  • /moat asks the model to answer in the language currently configured with /lang
  • if LLM call fails, the app logs the error and falls back to a deterministic template
  • /model accepts any valid provider model ID; the built-in suggestion list contains verified official IDs.
  • for maximum compatibility, you can set explicit IDs like provider/model (example: openai/gpt-5, anthropic/claude-sonnet-4-5, gemini/gemini-2.5-pro)
  • Your selected model is persisted in ~/.graham/config.json.

Environment variables (depending on provider):

  • OPENAI_API_KEY
  • ANTHROPIC_API_KEY
  • GEMINI_API_KEY or GOOGLE_API_KEY

Examples:

/model gpt-5.2
/model claude-opus-4-5
/model gemini-3-pro-preview

Configure API keys in your shell before launching graham:

export OPENAI_API_KEY="sk-..."
export ANTHROPIC_API_KEY="..."
export GEMINI_API_KEY="..."
graham

🌍 Display Language

  • Default display language is English (en).
  • List supported language codes:
/languages
  • Change language at runtime with:
/lang fr
  • Translation uses deep-translator (Google Translate backend), not LLMs.
  • If translation fails for any reason, the app keeps English text (safe fallback).
  • Your chosen language is persisted in ~/.graham/config.json.

🎯 Score Rating

  • The ranking now includes a visual rating badge:
    • 🟢 if score is above the green threshold
    • 🟠 if score is between orange and green thresholds
    • 🔴 otherwise
  • Configure thresholds at runtime:
/rating 0.80 0.60

You can also pass percentages:

/rating 80 60

🌐 Universe Presets

Available universe files now include:

  • sample
  • world
  • usa
  • emerging_markets
  • china
  • india
  • germany
  • europe
  • france
  • japan

Set and persist your default universe:

/default-universe world

Your latest selected universe is saved in ~/.graham/config.json and automatically reused on next launch.

List all universes with metadata:

/universes

Load a supported index and fetch constituents via yfinance:

/indices sp500

🗂️ Project Structure

graham/
  main.py
  tui.py
  graham.py
  commands.py
  llm.py
universes/
  sample.txt
tests/
  test_graham.py

🧪 Tests

pytest

🚀 Releases

Release process (GitHub + PyPI): see RELEASING.md.

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

graham_agent-0.3.4.tar.gz (57.6 kB view details)

Uploaded Source

Built Distribution

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

graham_agent-0.3.4-py3-none-any.whl (48.5 kB view details)

Uploaded Python 3

File details

Details for the file graham_agent-0.3.4.tar.gz.

File metadata

  • Download URL: graham_agent-0.3.4.tar.gz
  • Upload date:
  • Size: 57.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for graham_agent-0.3.4.tar.gz
Algorithm Hash digest
SHA256 db141974514ecece77b5c4662f073802d75c63eec14cc28acdaef103f550566c
MD5 353d96f1de8fb8af3493d95e54570625
BLAKE2b-256 f8bcad4995855054647066bff123d742f0637b15aeded9798db7e94bcbef8dc1

See more details on using hashes here.

Provenance

The following attestation bundles were made for graham_agent-0.3.4.tar.gz:

Publisher: release.yml on fdelbrayelle/graham

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

File details

Details for the file graham_agent-0.3.4-py3-none-any.whl.

File metadata

  • Download URL: graham_agent-0.3.4-py3-none-any.whl
  • Upload date:
  • Size: 48.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for graham_agent-0.3.4-py3-none-any.whl
Algorithm Hash digest
SHA256 8f423e28750471ddedfea2bcf23949d101598299485bd8dbed814e194c349e6e
MD5 9e379ee4e66521aeeeea9d13eb7af482
BLAKE2b-256 6329286651002c3e91b7d1398bc2d44a9db20b41b6165861a3043459902baa93

See more details on using hashes here.

Provenance

The following attestation bundles were made for graham_agent-0.3.4-py3-none-any.whl:

Publisher: release.yml on fdelbrayelle/graham

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