Skip to main content
image

👤 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.

Metadata

Release files for graham-agent 0.3.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for graham-agent 0.3.5
File Size Uploaded
graham_agent-0.3.5.tar.gz 57.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for graham-agent 0.3.5
File Interpreter ABI Platform
graham_agent-0.3.5-py3-none-any.whl Python 3 none any Details

Total release size: 106.1 kB

Release files / graham_agent-0.3.5.tar.gz

Download URL graham_agent-0.3.5.tar.gz
Size 57.6 kB
Tags Source
SHA-256 checksum
How to use checksums
ddcf2ca7e82556ccce36e693699cfbaa06dbe93f0a0836488770d4f0882e69bd
BLAKE2b-256 checksum
How to use checksums
6869bcf99ed523696bd868093e9777215c38a627b2eb087d6dcbf337c4db3555
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 21, 2026.

Transparency log

Release files / graham_agent-0.3.5-py3-none-any.whl

Download URL graham_agent-0.3.5-py3-none-any.whl
Size 48.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
313a9aa777eaa0861c056a66cdd3a21f372a0bc3cce13ad26c981f683934043c
BLAKE2b-256 checksum
How to use checksums
e047dee2c728c5abe810aec3ffab89a034a84a87f3e716f63a2e50f9fb5b01fa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Feb 21, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.5 This release

2 release files

0.3.4

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page