Metadata-Version: 2.4
Name: screener-ai-tool
Version: 0.1.0
Summary: Async screener.in scraper with MCP server and CLI for NSE/BSE stock data
Project-URL: Homepage, https://github.com/singhvedant/screener-ai-tool
Project-URL: Repository, https://github.com/singhvedant/screener-ai-tool
Project-URL: Issues, https://github.com/singhvedant/screener-ai-tool/issues
Author: Vedant Singh
License: MIT
License-File: LICENSE
Keywords: bse,finance,llm,mcp,nse,screener.in,stocks
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business :: Financial :: Investment
Requires-Python: >=3.11
Requires-Dist: beautifulsoup4>=4.12
Requires-Dist: httpx>=0.27
Requires-Dist: lxml>=5.0
Requires-Dist: mcp[cli]>=1.2
Requires-Dist: pandas>=2.2
Requires-Dist: pyarrow>=16.0
Requires-Dist: pydantic>=2.7
Requires-Dist: rich>=13.0
Requires-Dist: typer>=0.12
Requires-Dist: uvicorn>=0.30
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-timeout>=2.3; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Description-Content-Type: text/markdown

# screener-ai-tool

An async Python toolkit for fetching and analyzing Indian (NSE/BSE) stock data
from [screener.in](https://www.screener.in) — fundamentals, price history,
technical indicators, peer comparisons, shareholding patterns, and corporate
documents/announcements. Ships as a CLI (`screener`), a library
(`screener_ai`), and an MCP server for use with Claude and other LLM clients.

> **Disclaimer**: This is a personal-use tool for analyzing publicly visible
> data on screener.in. It is not affiliated with or endorsed by screener.in.
> Be polite: respect screener.in's Terms of Service, keep request rates low
> (defaults are conservative), prefer an authenticated session for heavier
> usage, and do not use this tool to circumvent rate limits or scrape at
> scale. You are responsible for how you use this tool.

## Architecture

```
                         ┌───────────────────────────┐
                         │        screener.in         │
                         │  (HTML pages + JSON APIs)   │
                         └──────────────┬─────────────┘
                                         │ httpx (rate-limited, retried)
                                         ▼
                         ┌───────────────────────────┐
                         │       client.py            │
                         │  ScreenerClient             │
                         │  - RateLimiter (token bkt)  │
                         │  - retries + backoff        │
                         │  - cookie jar / session      │
                         └──────────────┬─────────────┘
                                         │
                ┌────────────────────────┼─────────────────────────┐
                ▼                        ▼                          ▼
        ┌──────────────┐        ┌───────────────┐          ┌──────────────┐
        │  parsers.py   │        │  auth.py       │          │ indicators.py │
        │  HTML/JSON →  │        │  login/session │          │ SMA/EMA/RSI/  │
        │  pydantic     │        │  persistence   │          │ MACD/Bollinger│
        │  models       │        │                │          │ /Stochastic   │
        └──────┬────────┘        └───────┬────────┘          └──────┬────────┘
                │                          │                          │
                └────────────┬─────────────┴────────────┬────────────┘
                              ▼                          ▼
                      ┌───────────────────────────────────────┐
                      │             service.py                  │
                      │           ScreenerService                │
                      │  high-level facade (search, overview,   │
                      │  price_history, financials, peers,      │
                      │  shareholding, documents, technical,     │
                      │  snapshot) — page-cache(8) per instance  │
                      └───────┬───────────────────┬─────────────┘
                               │                   │
                ┌──────────────▼───┐     ┌─────────▼──────────┐
                │     bulk.py        │     │     export.py       │
                │  bulk_fetch over   │     │  JSON/CSV/Parquet    │
                │  many symbols      │     │  export              │
                │  (semaphore-bound) │     │                      │
                └──────────────┬─────┘     └─────────┬───────────┘
                               │                       │
                ┌──────────────▼───────────────────────▼───────────┐
                │                  Interfaces                        │
                │  ┌────────────────────┐   ┌──────────────────────┐│
                │  │     cli.py          │   │   mcp_server.py       ││
                │  │  `screener ...`     │   │  FastMCP server        ││
                │  │  typer + rich       │   │  `screener_*` tools     ││
                │  └────────────────────┘   └──────────────────────┘│
                └─────────────────────────────────────────────────────┘
```

## Installation

### Have your agent install it (recommended)

This repo ships an [`llms.txt`](llms.txt) written for AI coding agents. Paste
this into your agent (Claude Code, Cursor, Codex, etc.):

```
Fetch and follow the instructions at
https://raw.githubusercontent.com/singhvedant/screener-ai-tool/main/llms.txt
to install screener-ai-tool, register its MCP server, and install its skill.
```

The agent will: install the package (`pip`/`uv`), register the `screener` MCP
server, copy `skills/screener-ai-tool/SKILL.md` into its own skills directory
(so the skill can be toggled on/off independently later), and verify
everything works end-to-end.

### Manual installation

Requires Python 3.11+.

```bash
pip install screener-ai-tool
# or
uv pip install screener-ai-tool
```

Or from source, with [`uv`](https://docs.astral.sh/uv/):

```bash
git clone https://github.com/singhvedant/screener-ai-tool.git
cd screener-ai-tool

# create a virtual environment and install in editable mode with dev deps
uv venv
source .venv/bin/activate
uv pip install -e ".[dev]"
```

After installation the `screener` CLI entry point is available (or run via
`uv run screener ...` / `python -m screener_ai ...`).

## Authentication & session persistence

screener.in allows limited anonymous browsing (~50 page views/day) before
showing rate-limit prompts. Logging in with your screener.in account raises
those limits significantly.

```bash
# interactive (prompts for password, hidden input)
screener login --email you@example.com

# or via environment variables (useful for CI / scripts)
export SCREENER_EMAIL=you@example.com
export SCREENER_PASSWORD=your-password
screener login

# check status
screener auth-status

# log out (clears saved session)
screener logout
```

On success, the session cookie (`sessionid`) is written to
`~/.screener-ai/session.json` (mode `0600`, JSON of cookie name → value). All
subsequent CLI/MCP/library calls reuse this session automatically until it
expires or `screener logout` is run. Credentials and cookies are never
logged.

## CLI usage

All commands support `--json` for raw JSON output (useful for piping into
`jq` or other tools), and most accept `--days` / `--consolidated`/`--standalone`
where relevant. Async commands wrap `asyncio.run`; errors print a message to
stderr and exit with code 1 (authentication errors suggest `screener login`).

### `screener search QUERY`

Search for companies by name or symbol.

```bash
screener search reliance
screener search tata --json
```

### `screener price SYMBOL [--days 365] [--limit 30]`

Show recent price history (rich table of the latest `--limit` rows).

```bash
screener price RELIANCE
screener price RELIANCE --days 90 --limit 10
screener price TCS --json > tcs_prices.json
```

### `screener overview SYMBOL`

Company overview: name, sector/industry, key ratios, pros/cons.

```bash
screener overview RELIANCE
screener overview INFY --json
```

### `screener fundamentals SYMBOL [--statement profit-loss|quarters|balance-sheet|cash-flow|ratios]`

Fetch a financial statement table.

```bash
screener fundamentals RELIANCE --statement quarters
screener fundamentals RELIANCE --statement balance-sheet --json
```

### `screener technical SYMBOL [--days 365] [--series]`

Technical analysis snapshot (SMA/EMA/RSI/MACD/Bollinger + signals). Pass
`--series` to include full indicator time series.

```bash
screener technical RELIANCE
screener technical RELIANCE --days 180 --series --json
```

### `screener events SYMBOL` / `screener news SYMBOL`

`events` shows announcements + corporate documents; `news` is an alias view
focused on announcements, credit ratings, and concall material.

```bash
screener events RELIANCE
screener news RELIANCE --json
```

### `screener peers SYMBOL`

Peer comparison table (with median row).

```bash
screener peers RELIANCE
```

### `screener shareholding SYMBOL`

Quarterly and yearly shareholding pattern tables.

```bash
screener shareholding RELIANCE
```

### `screener snapshot SYMBOL`

Aggregated "everything we know" snapshot — overview, financial tables,
shareholding, peers, documents, and technical analysis, fetched
concurrently.

```bash
screener snapshot RELIANCE --json
```

### `screener bulk SYMBOLS... [--dataset price] [--concurrency 8] [--out FILE]`

Fetch a dataset for many symbols concurrently. Per-symbol failures are
captured (never raised) as `{"error": "..."}` entries.

```bash
screener bulk RELIANCE TCS INFY HDFCBANK --dataset overview
screener bulk RELIANCE TCS INFY --dataset price --days 90 --concurrency 16 --out prices.json
```

### `screener export SYMBOL [--what price|fundamentals|snapshot] [--fmt json|csv|parquet] [--out FILE]`

Export data to JSON, CSV, or Parquet (CSV/Parquet for tabular data such as
price history and financial tables via pandas).

```bash
screener export RELIANCE --what price --fmt csv --out reliance_price.csv
screener export RELIANCE --what snapshot --fmt json --out reliance_snapshot.json
```

### `screener serve [--transport stdio|http] [--host 127.0.0.1] [--port 8631]`

Run the MCP server (see below).

## MCP setup

### stdio transport (recommended for Claude Code / Claude Desktop)

```bash
# if installed via pip/uv (entry point on PATH)
claude mcp add screener -- screener serve

# if running from a cloned repo
claude mcp add screener -- uv run --directory ~/Development/screener-ai-tool screener serve
```

This registers a `screener` MCP server that Claude launches on demand via
`stdio`.

### HTTP transport

To run the server over streamable HTTP (e.g. for remote access or shared
deployments):

```bash
uv run --directory ~/Development/screener-ai-tool screener serve --transport http --host 127.0.0.1 --port 8631
```

Then point an MCP-compatible client at `http://127.0.0.1:8631`.

## MCP tool list

All tools return JSON-serializable dicts (`model_dump`).

| Tool | Description |
| --- | --- |
| `screener_login(email, password)` | Authenticate and persist a session (use when rate-limited as anonymous) |
| `screener_auth_status()` | Current authentication status |
| `screener_search(query, limit=10)` | Search for companies |
| `screener_price(symbol, days=365, limit=0)` | Price history (optionally truncated to last `limit` points) |
| `screener_overview(symbol)` | Company overview + key ratios |
| `screener_fundamentals(symbol, statement="profit-loss")` | Financial statement table |
| `screener_technical(symbol, days=365, include_series=False)` | Technical indicators + signals |
| `screener_peers(symbol)` | Peer comparison table |
| `screener_shareholding(symbol)` | Shareholding pattern (quarterly/yearly) |
| `screener_events(symbol)` | Announcements + corporate documents |
| `screener_news(symbol)` | Announcements + credit ratings + concalls |
| `screener_snapshot(symbol, days=365)` | Full aggregated snapshot |
| `screener_bulk(symbols, dataset="price", days=365, concurrency=8)` | Bulk fetch over many symbols |
| `screener_export(symbol, what="snapshot", fmt="json", out_path="")` | Export data to a file |

## Data models

Defined with pydantic v2 in `screener_ai.models`:

- `SearchResult` — id, name, url, symbol (parsed from url)
- `CompanyRef` — symbol, name, company_id, warehouse_id, consolidated
- `PricePoint` / `PriceHistory` — date, close, dma50, dma200, volume, delivery_pct
- `KeyRatios` — market_cap, current_price, high_52w, low_52w, pe, book_value, dividend_yield, roce, roe, face_value, extras
- `CompanyOverview` — ref, about, website, bse_code, nse_code, sector, industry, ratios, pros, cons
- `FinancialTable` — kind, columns, rows (raw strings) + `.numeric()` helper
- `ShareholdingPattern` — quarterly / yearly `FinancialTable`
- `PeerRow` / `PeerComparison` — peer metrics + median row
- `DocumentLink` / `CorporateDocs` — announcements, annual reports, credit ratings, concalls
- `IndicatorSeries`, `MACDResult`, `BollingerResult` — technical indicator series
- `TechnicalSnapshot` / `TechnicalAnalysis` — latest indicator values + signals
- `FullSnapshot` — everything aggregated for one symbol

## Rate limiting & politeness

- Default: **2 requests/second**, burst of **5** (token-bucket `RateLimiter`).
- On HTTP 429, the client backs off exponentially (with jitter) and honors
  `Retry-After`; if retries are exhausted, `RateLimitError` is raised.
- Connection pooling is bounded (`max_connections=20`, `keepalive=10`).
- Anonymous browsing is heavily throttled by screener.in (~50 page views/day).
  **Log in** (`screener login`) for a much higher effective limit, and
  consider lowering `--concurrency` for `bulk`/`snapshot` operations.
- `bulk_fetch` / `screener bulk` isolate per-symbol errors — one bad symbol
  never aborts the whole batch.

## Testing

```bash
# unit tests only (no network) — the default safe subset
pytest -m "not integration and not load"

# live integration tests (hits screener.in; skips gracefully on network errors)
pytest -m integration

# heavy load test (500+ symbols; requires network and takes a while)
pytest -m load
```

Unit tests use captured fixtures in `tests/fixtures/` (real screener.in HTML
and JSON responses) plus `respx` to mock `httpx` for client-level tests — no
network access is required for `pytest -m "not integration and not load"`.

## License

MIT — see [LICENSE](LICENSE).
