pybhav
pybhav is a Python library for downloading NSE (National Stock Exchange of India) bhavcopy files — the official end-of-day market data for Indian equities, futures & options, currency derivatives, and SME equities.
Features
- Single-day and date-range downloads — get one or many trading days as a pandas DataFrame
- Save to disk — download the raw CSV file directly to a local directory
- Automatic caching — re-uses previously downloaded files; no redundant network calls
- Retry with backoff — handles transient HTTP errors gracefully
- NSE session management — warms up the required cookies and browser headers automatically
- Extensible by design — swap the cache, fetcher, or parser via constructor injection (SOLID)
Installation
pip install pybhav
Requirements: Python 3.9+, requests >= 2.28, pandas >= 1.5
Quick Start
from pybhav import NSEBhavcopy
nse = NSEBhavcopy()
# Single trading day → pandas DataFrame
df = nse.get("2025-06-30")
print(df.head())
# Date range → combined DataFrame (weekends/holidays skipped automatically)
df = nse.get_range("2025-06-01", "2025-06-30")
# Save raw CSV to disk → returns the Path to the written file
path = nse.download("2025-06-30", dest="./data/")
print(path) # data/cm_2025-06-30_bhav.csv
Market Segments
Pass the segment keyword to any method. Defaults to "CM" (equities).
| Key | Market | Exchange Section |
|---|---|---|
CM |
Capital Market | NSE Equities (default) |
FO |
Futures & Options | NSE Derivatives |
CD |
Currency Derivatives | NSE Currency |
SME |
SME Equities | NSE Emerge (SME platform) |
# Futures & Options bhavcopy
df = nse.get("2025-06-30", segment="FO")
# Currency derivatives for a range
df = nse.get_range("2025-06-01", "2025-06-30", segment="CD")
API Reference
NSEBhavcopy(cache_dir, retries, timeout, *, cache, fetcher, parser)
The main client. All arguments are optional.
| Parameter | Type | Default | Description |
|---|---|---|---|
cache_dir |
str | Path | None |
~/.pybhav_cache |
Directory for the file cache. Pass None to disable caching. |
retries |
int |
3 |
Number of retry attempts on transient HTTP failures. |
timeout |
int |
30 |
HTTP request timeout in seconds. |
cache |
BhavcopCache |
FileCache |
Override the cache with any BhavcopCache implementation. |
fetcher |
BhavcopFetcher |
NSEHttpFetcher |
Override the HTTP layer with any BhavcopFetcher. |
parser |
BhavcopParser |
NSECsvParser |
Override the parser with any BhavcopParser. |
nse.get(dt, segment="CM") → pd.DataFrame
Download bhavcopy for a single trading date and return it as a DataFrame.
from datetime import date
df = nse.get("2025-06-30") # string date
df = nse.get(date(2025, 6, 30)) # date object
df = nse.get("2025-06-30", segment="FO")
Raises BhavcopNotAvailable if the date is a weekend, public holiday, or future date.
nse.get_range(start, end, segment="CM", skip_errors=True) → pd.DataFrame
Download bhavcopy for a date range and concatenate into a single DataFrame.
A _date column is added to each row indicating which trading date it came from.
df = nse.get_range("2025-06-01", "2025-06-30")
# Raise on the first unavailable date instead of skipping
df = nse.get_range("2025-06-01", "2025-06-30", skip_errors=False)
| Parameter | Default | Description |
|---|---|---|
start |
— | Start date, inclusive. date object or "YYYY-MM-DD" string. |
end |
— | End date, inclusive. |
segment |
"CM" |
Market segment. |
skip_errors |
True |
Skip weekends/holidays silently. Set False to raise instead. |
Returns an empty DataFrame if no data was found for any date in the range.
nse.download(dt, segment="CM", dest=".") → Path
Download the bhavcopy CSV for a single date to a local directory.
path = nse.download("2025-06-30") # saves to current dir
path = nse.download("2025-06-30", segment="FO", dest="./data/fo/")
print(path) # ./data/fo/fo_2025-06-30_bhav.csv
The destination directory is created if it does not exist. Returns the Path of the written file. If the data is already in the local cache, no network request is made.
Configuration Examples
Disable caching
nse = NSEBhavcopy(cache_dir=None)
Custom cache directory and longer timeout
nse = NSEBhavcopy(cache_dir="/mnt/data/bhav_cache", timeout=60)
More retries for flaky networks
nse = NSEBhavcopy(retries=5)
Extending pybhav
pybhav is built on three abstract base classes exported from the package. Implement any of them and inject it into NSEBhavcopy to replace the default behaviour without touching library code.
Custom cache (e.g. in-memory)
from datetime import date
from pybhav import BhavcopCache, NSEBhavcopy
class MemoryCache(BhavcopCache):
def __init__(self):
self._store: dict[tuple, bytes] = {}
def has(self, segment: str, dt: date) -> bool:
return (segment, dt) in self._store
def get(self, segment: str, dt: date) -> bytes:
return self._store[(segment, dt)]
def put(self, segment: str, dt: date, data: bytes) -> None:
self._store[(segment, dt)] = data
nse = NSEBhavcopy(cache=MemoryCache())
Custom parser (e.g. select and rename columns)
import io
import pandas as pd
from pybhav import BhavcopParser, NSEBhavcopy
class SlimParser(BhavcopParser):
_COLS = {"SYMBOL": "symbol", "OPEN": "open", "HIGH": "high",
"LOW": "low", "CLOSE": "close", "TOTTRDQTY": "volume"}
def parse(self, data: bytes) -> pd.DataFrame:
df = pd.read_csv(io.BytesIO(data))
df.columns = df.columns.str.strip()
return df[[c for c in self._COLS if c in df.columns]].rename(columns=self._COLS)
nse = NSEBhavcopy(parser=SlimParser())
df = nse.get("2025-06-30")
print(df.columns.tolist()) # ['symbol', 'open', 'high', 'low', 'close', 'volume']
Custom fetcher (e.g. load from local files for backtesting)
from datetime import date
from pathlib import Path
from pybhav import BhavcopFetcher, NSEBhavcopy
from pybhav.exceptions import BhavcopNotAvailable
class LocalFileFetcher(BhavcopFetcher):
def __init__(self, directory: str):
self._dir = Path(directory)
def fetch(self, segment: str, dt: date) -> bytes:
path = self._dir / f"{segment.lower()}_{dt.isoformat()}_bhav.csv"
if not path.exists():
raise BhavcopNotAvailable(f"No local file for {segment}/{dt}")
return path.read_bytes()
nse = NSEBhavcopy(fetcher=LocalFileFetcher("./local_data/"))
df = nse.get("2025-06-30")
Error Handling
All exceptions inherit from PybhavError.
| Exception | When raised |
|---|---|
BhavcopNotAvailable |
The requested date is a weekend, holiday, or future date (HTTP 404). |
DownloadError |
HTTP request failed after all retry attempts. |
SessionError |
Could not establish a valid NSE session (homepage unreachable). |
from pybhav import NSEBhavcopy, BhavcopNotAvailable, DownloadError
nse = NSEBhavcopy()
try:
df = nse.get("2025-06-28") # Saturday
except BhavcopNotAvailable:
print("Market closed on this date.")
except DownloadError as e:
print(f"Network error: {e}")
Architecture
pybhav follows SOLID principles. The main components are:
NSEBhavcopy (client.py)
├── BhavcopCache → FileCache / NullCache (cache.py)
├── BhavcopFetcher → NSEHttpFetcher (fetcher.py)
│ └── _ZipExtractor
└── BhavcopParser → NSECsvParser (parser.py)
Supporting utilities
├── session.py — NSE cookie/header warm-up
├── urls.py — URL construction per segment
└── exceptions.py — PybhavError hierarchy
Each dependency is an abstract base class (protocols.py). The defaults work out of the box; pass your own implementation to any of the three constructor kwargs to override.
Development
# Clone and install in editable mode with dev dependencies
git clone https://github.com/prashant-fintech/pybhav.git
cd pybhav
pip install -e ".[dev]"
# Run tests
pytest
License
MIT © Prashant Singh
Metadata
Release files for pybhav 0.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pybhav-0.0.2.tar.gz | 21.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pybhav-0.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 41.3 kB
Release files / pybhav-0.0.2.tar.gz
| Download URL | pybhav-0.0.2.tar.gz |
|---|---|
| Size | 21.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7affd94abec005dcfa7fedf52f1b4cbc62a174ac609e1b18bc0a3081602d838f
|
|
BLAKE2b-256 checksum How to use checksums |
66a117811a4cb088786d2cb5e5785a62d1223b4bae411d073328370462f7b53c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.6
|
Release files / pybhav-0.0.2-py3-none-any.whl
| Download URL | pybhav-0.0.2-py3-none-any.whl |
|---|---|
| Size | 20.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c593b9fb2a70a73ebf33dca999a740b6b96c45a586cadaca539054191616e47a
|
|
BLAKE2b-256 checksum How to use checksums |
a6ed3702f32d6cb21e5bdd5df36a60ee93c45f0a5ba9411fdab21fe501bcc4a1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.6
|