Download NSE bhavcopy (end-of-day market data) for Indian equities and derivatives
Project description
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pybhav-0.0.1.tar.gz.
File metadata
- Download URL: pybhav-0.0.1.tar.gz
- Upload date:
- Size: 18.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6dec5b37d4b45b9761b079638febd91430ab39701595a4ff5afd5122cd33f903
|
|
| MD5 |
4cddb2ea4f1603fcb18774abcf015262
|
|
| BLAKE2b-256 |
1ff2ed9ffb49ff89b195afb95a7d9ec232f28bbda856de4333d9bc9606095368
|
File details
Details for the file pybhav-0.0.1-py3-none-any.whl.
File metadata
- Download URL: pybhav-0.0.1-py3-none-any.whl
- Upload date:
- Size: 18.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
90eca8b3020dbdda4d1221f2e5f86de39a69d2a1340503cb9f7c8749922d84d8
|
|
| MD5 |
0b003456b107035fbfffcb3cc5b9fc60
|
|
| BLAKE2b-256 |
876af69fbd6be89ce8301657e5af953dc8449ef973df163440f707705ac00857
|