Metadata-Version: 2.4
Name: nsefetch
Version: 0.1.0
Summary: Production-grade NSE data engine for Python. Cache-first, circuit-protected, async-ready.
Author-email: Harsh Kumar Rai <harshkumarrai@protonmail.com>
License: MIT
Project-URL: Homepage, https://github.com/harsh-kumar-rai/nsefetch
Project-URL: Repository, https://github.com/harsh-kumar-rai/nsefetch
Project-URL: Issues, https://github.com/harsh-kumar-rai/nsefetch/issues
Project-URL: Changelog, https://github.com/harsh-kumar-rai/nsefetch/blob/main/CHANGELOG.md
Keywords: nse,nse-india,stock,market,india,finance,trading,options,derivatives,equity,algotrading
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial :: Investment
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: curl-cffi<1,>=0.11
Requires-Dist: httpx<1,>=0.27
Requires-Dist: pydantic<3,>=2
Requires-Dist: pydantic-settings<3,>=2
Requires-Dist: redis<7,>=5
Provides-Extra: dev
Requires-Dist: pytest<9,>=8; extra == "dev"
Requires-Dist: pytest-asyncio<1,>=0.23; extra == "dev"
Requires-Dist: ruff<1,>=0.4; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Dynamic: license-file

# nsefetch

> Production-grade NSE data engine for Python. Cache-first, circuit-protected, async-ready.

[![CI](https://github.com/harsh-kumar-rai/nsefetch/actions/workflows/ci.yml/badge.svg)](https://github.com/harsh-kumar-rai/nsefetch/actions/workflows/ci.yml)
[![PyPI version](https://img.shields.io/pypi/v/nsefetch.svg)](https://pypi.org/project/nsefetch/)
[![Python](https://img.shields.io/pypi/pyversions/nsefetch.svg)](https://pypi.org/project/nsefetch/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

```bash
pip install nsefetch
```

---

## Quick Start

```python
from nsefetch import MarketService

with MarketService() as service:
    quote = service.get_stock("RELIANCE")
    print(quote.data.last_price)         # e.g. 2947.35

    chain = service.get_option_chain("NIFTY")
    print(chain.data.expiry)             # nearest expiry date
```

---

## Features

- ⚡ **Cache-first runtime** — in-process or Redis-backed, with per-operation TTLs
- 🔌 **Circuit breaker** — automatic failure isolation and recovery
- 🔄 **Request coalescing** — deduplicates concurrent identical requests within a process
- 🚦 **GCRA rate limiter** — Redis or in-memory, prevents NSE from rate-limiting you
- 🌊 **Stale fallback** — serves cached data gracefully on live fetch failure
- ⚙️ **Async parity** — every sync method has an exact async equivalent
- 🛡️ **Typed responses** — Pydantic v2 models on every response, always a `ResponseEnvelope[T]`
- 🧪 **131 tests** — zero live-network calls in normal test runs
- 🔒 **NSE-only** — no silent Yahoo Finance substitution; you always know what you're getting

---

## API Reference

All methods are available on both `MarketService` (sync) and `AsyncMarketService` (async).

| Method | Description |
|---|---|
| `get_nifty50()` | NIFTY 50 index snapshot |
| `get_index(index_name)` | Any supported NSE index snapshot |
| `get_stock(symbol)` | Single-symbol equity quote |
| `get_bulk_stocks(symbols)` | Multi-symbol equity quotes with smart batching |
| `get_option_chain(symbol, expiry=None)` | Option chain; defaults to nearest expiry |
| `get_option_chain_expiries(symbol)` | All available option expiry dates |
| `get_futures_data(symbol)` | F&O futures snapshot |
| `get_delivery_data(symbol)` | Delivery / DPIIT data |
| `get_market_depth(symbol)` | Level-2 order book (optional data) |
| `get_sector_data()` | All NSE sector snapshots |

Every method returns `ResponseEnvelope[T]` with:

```python
envelope.success        # bool
envelope.data           # typed payload
envelope.cache_hit      # bool — was this served from cache?
envelope.stale          # bool — was this a stale cache fallback?
envelope.degraded       # bool — did a backend component fail?
envelope.fetched_at     # datetime
envelope.warnings       # list of WarningInfo
```

---

## Runtime Modes

### Direct (default)

Simple. No Redis, no background infrastructure needed.

```python
from nsefetch import MarketService

service = MarketService()
quote = service.get_stock("RELIANCE")
service.close()

# or use as a context manager
with MarketService() as service:
    quote = service.get_stock("RELIANCE")
```

### Cached (scripts & repeated polling)

Adds in-process LRU cache + rate limiter. Best for local scripts that poll repeatedly.

```python
from nsefetch import create_cached_market_service

with create_cached_market_service() as service:
    # First call hits NSE; subsequent calls within TTL hit cache
    quote = service.get_stock("RELIANCE")
```

### Production (Redis-backed)

For dashboard backends and multi-worker deployments. Keeps frontend traffic reading from cache instead of hammering NSE directly.

```python
from nsefetch import create_production_market_service

with create_production_market_service() as service:
    snapshot = service.get_nifty50()
```

### Async

All three modes have async counterparts:

```python
from nsefetch import AsyncMarketService, create_async_cached_market_service

async with AsyncMarketService() as service:
    quote = await service.get_stock("INFY")

async with create_async_cached_market_service() as service:
    chain = await service.get_option_chain("BANKNIFTY")
```

---

## Option Chain with Specific Expiry

```python
with MarketService() as service:
    # Discover all available expiries
    expiries = service.get_option_chain_expiries("NIFTY")
    print(expiries.data)   # ['2026-05-29', '2026-06-26', ...]

    # Fetch a specific expiry
    chain = service.get_option_chain("NIFTY", expiry="2026-06-26")
```

---

## Architecture

```mermaid
graph TD
    A[MarketService / AsyncMarketService] --> B[Rate Limiter]
    B --> C[Cache Layer]
    C -->|cache miss| D[Circuit Breaker]
    D --> E[NSE HTTP Client]
    E --> F[curl_cffi Transport]
    F --> G[NSE API]
    C -->|cache hit| H[ResponseEnvelope]
    D -->|open| I[Stale Fallback Cache]
    I --> H
```

| Layer | Component | Purpose |
|---|---|---|
| Service | `MarketService` | Public API, context manager lifecycle |
| Reliability | `CircuitBreaker` | Blocks requests when provider is failing |
| Reliability | `RateLimiter` | GCRA — prevents NSE from blocking you |
| Caching | `CacheManager` | In-memory or Redis, request coalescing |
| Transport | `NSEHttpClient` | Browser-impersonating session (curl_cffi) |
| Normalizer | `NSEMarketDataNormalizer` | Raw payload → typed Pydantic schemas |

---

## Configuration

All settings are configurable via environment variables prefixed with `NSEFETCH_`:

```bash
# Redis connection (for production/cached modes)
NSEFETCH_REDIS_URL=redis://localhost:6379/0

# Cache TTLs (seconds)
NSEFETCH_CACHE_TTL_STOCK=15
NSEFETCH_CACHE_TTL_INDEX=10
NSEFETCH_CACHE_TTL_OPTION_CHAIN=30

# Circuit breaker
NSEFETCH_CIRCUIT_BREAKER_FAILURE_THRESHOLD=5
NSEFETCH_CIRCUIT_BREAKER_RECOVERY_WINDOW=60

# Rate limiter
NSEFETCH_RATE_LIMIT_REQUESTS_PER_SECOND=2

# Live probe gate
NSEFETCH_LIVE_PROBE_ENABLED=1
```

Or pass a `Settings` object directly:

```python
from nsefetch import MarketService, Settings

settings = Settings(redis_url="redis://localhost:6379/0")
service = MarketService(settings=settings)
```

---

## Live Probe

Test your connectivity to NSE before deploying:

```bash
# Transport-level probe
NSEFETCH_LIVE_PROBE_ENABLED=1 python -m nsefetch.live_probe

# Service-level probe (exercises all 10 methods)
NSEFETCH_LIVE_PROBE_ENABLED=1 python -m nsefetch.live_probe --service

# Both together
NSEFETCH_LIVE_PROBE_ENABLED=1 python -m nsefetch.live_probe --all
```

---

## Contributing

Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for setup instructions, coding standards, and PR guidelines.

Good first issues include: new index names, BSE provider, pandas integration, and export utilities.

---

## Legal

This library is for **educational and research purposes only**. It is not affiliated with, endorsed by, or connected to the National Stock Exchange of India (NSE). Use at your own risk.

See [DISCLAIMER.md](DISCLAIMER.md) for the full legal notice and [LICENSE](LICENSE) for the MIT license.
