Skip to main content

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.

Python License: MIT PyPI


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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pybhav-0.0.1.tar.gz (18.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pybhav-0.0.1-py3-none-any.whl (18.4 kB view details)

Uploaded Python 3

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

Hashes for pybhav-0.0.1.tar.gz
Algorithm Hash digest
SHA256 6dec5b37d4b45b9761b079638febd91430ab39701595a4ff5afd5122cd33f903
MD5 4cddb2ea4f1603fcb18774abcf015262
BLAKE2b-256 1ff2ed9ffb49ff89b195afb95a7d9ec232f28bbda856de4333d9bc9606095368

See more details on using hashes here.

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

Hashes for pybhav-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 90eca8b3020dbdda4d1221f2e5f86de39a69d2a1340503cb9f7c8749922d84d8
MD5 0b003456b107035fbfffcb3cc5b9fc60
BLAKE2b-256 876af69fbd6be89ce8301657e5af953dc8449ef973df163440f707705ac00857

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page