One Python client for market data — crypto, Indian equities & options.
Same call everywhere. Get a pandas DataFrame or typed bars in three lines.
from bandl import Bandl, Interval
df = Bandl().crypto.get_ohlcv_dataframe("BTC/USDT", Interval.D1) # no API key needed
One client, one API. Switch markets by changing the symbol — not your code.
- 🟢 Zero-config crypto — Binance & CoinDCX public data, no keys.
- 🇮🇳 Indian equities & indices —
RELIANCE,NIFTY 50,BANKNIFTYvia Zerodha. - 📈 Options, incl. expired contracts — NSE/BSE F&O + MCX commodities via Dhan.
- 🐼 pandas or typed models —
get_ohlcv_dataframe(...)orget_ohlcv(...) -> list[OHLCV]. - ⏱️ Normalized everywhere — UTC timestamps,
Decimalprices, oneIntervalenum. - 🤖 Agent-ready — a dedicated AGENTS.md reference for LLM tools.
Install
pip install bandl
Python 3.10+. Dev setup: pip install -e ".[dev]" (CONTRIBUTING.md).
60-second start
No API key required — crypto works out of the box:
from bandl import Bandl, Interval
client = Bandl()
df = client.crypto.get_ohlcv_dataframe("BTC/USDT", Interval.D1)
print(df.tail())
# timestamp open high low close volume
# 2025-01-10 00:00:00 94000.1 95200.0 92800.5 94850.2 12930.4451
Want a window? Pass start / end (UTC). Want raw bars instead of pandas? Call
get_ohlcv(...) — same arguments, returns list[OHLCV].
from datetime import datetime, timedelta, timezone
end = datetime.now(timezone.utc)
start = end - timedelta(days=30)
bars = client.crypto.get_ohlcv("ETHUSDT", Interval.H1, start, end)
print(bars[-1].close, bars[-1].source)
One API, every market
| Facet | Provider | Auth | Example symbols |
|---|---|---|---|
client.crypto |
binance |
None | BTC/USDT, ETHUSDT |
client.crypto |
coindcx |
None | BTCUSDT, ETHUSDT |
client.equity |
zerodha |
Kite key + token | RELIANCE, NIFTY 50, BANKNIFTY |
client.equity |
breeze |
ICICI Breeze key + session | RELIANCE, INFY |
client.derivatives |
dhan |
Dhan id + JWT | GOLDM26JUN145000CE, NIFTY26JAN24000PE |
client.derivatives |
breeze |
ICICI Breeze key + session | CRUDEOIL, GOLDM options & futures |
Every facet exposes the same two calls — get_ohlcv(...) and
get_ohlcv_dataframe(...). Pick a provider with source="...", or rely on each
facet's default.
Recipes
Indian equities & indices (Zerodha)
Add your Kite Connect credentials once; the rest
is identical to crypto. Symbol aliases (NIFTY 50 → NIFTY50) are handled for you.
from bandl import Bandl, BandlConfig, Interval, ProviderSettings
client = Bandl(BandlConfig(providers={
"zerodha": ProviderSettings(api_key="kite_api_key", access_token="daily_token"),
}))
reliance = client.equity.get_ohlcv_dataframe("RELIANCE", Interval.D1, source="zerodha")
nifty = client.equity.get_ohlcv_dataframe("NIFTY 50", Interval.D1, source="zerodha")
Options & derivatives (Dhan)
client.derivatives serves option OHLCV across NSE/BSE F&O and MCX commodities — with
open_interest on every bar. Give it a symbol string (auto-resolved against Dhan's
scrip master) or a structured OptionContract.
from datetime import date, datetime, timezone
from decimal import Decimal
from bandl import Bandl, BandlConfig, Interval, ProviderSettings
from bandl.models.market import OptionContract, OptionType
client = Bandl(BandlConfig(providers={
"dhan": ProviderSettings(api_key="dhan_client_id", access_token="dhan_jwt"),
}))
# 1) Symbol string — easiest
df = client.derivatives.get_ohlcv_dataframe(
"GOLDM26JUL145000CE", Interval.M5, source="dhan", exchange="MCX",
)
# 2) Structured contract — explicit & unambiguous
contract = OptionContract(
underlying="GOLDM", expiry=date(2026, 7, 29),
strike=Decimal("145000"), option_type=OptionType.CALL, exchange="MCX",
)
bars = client.derivatives.get_ohlcv(contract, Interval.M1, source="dhan")
# What expiries exist for an underlying?
expiries = client.derivatives.list_expiries("GOLDM", source="dhan", exchange="MCX")
Need expired contracts? Most APIs drop them. bandl still fetches their minute
candles — pass the native instrument_id once (look it up via Dhan, or the bundled
examples/dhan_expired_probe.py):
bars = client.derivatives.get_ohlcv(
"GOLDM26JUN143500CE", Interval.M1,
datetime(2026, 6, 26, tzinfo=timezone.utc),
datetime(2026, 6, 27, tzinfo=timezone.utc),
source="dhan", exchange="MCX", instrument_id="570800",
)
Indian equities, derivatives & commodities (ICICI Direct Breeze)
ICICI Direct Breeze provides historical OHLCV across equities, indices, and derivatives (including MCX commodities down to 1-second candles), along with Demat portfolio holdings and live order execution.
1. Obtaining API credentials & Session token
- Register/login on the ICICI Direct Breeze API Portal.
- Create an app to receive your App Key / API Key (
api_key) and Secret Key (api_secret). - To generate your daily session token:
- Open:
https://api.icicidirect.com/apiuser/login?api_key=<YOUR_API_KEY>in your browser. - Log in with your ICICI Direct credentials and TOTP.
- After login, your browser redirects to your registered Redirect URL with
?apisession=<session_token>. - Pass
<session_token>asaccess_tokenand your ICICI User ID asaccount_idinProviderSettings.
- Open:
2. Configuration & Market Data
from datetime import date, datetime, timezone
from decimal import Decimal
from bandl import Bandl, BandlConfig, Interval, ProviderSettings
from bandl.models.market import OptionContract, OptionType
client = Bandl(BandlConfig(providers={
"breeze": ProviderSettings(
api_key="your_app_key",
api_secret="your_secret_key",
access_token="your_daily_session_token",
account_id="your_user_id",
),
}))
# 1) Equities & Indices (NSE / BSE)
df_rel = client.equity.get_ohlcv_dataframe("RELIANCE", Interval.D1, source="breeze", exchange="NSE")
# 2) 1-Minute / 1-Second Candles for Commodities & Futures (MCX)
df_crude = client.equity.get_ohlcv_dataframe(
"CRUDEOIL", Interval.M1,
datetime(2026, 9, 20, tzinfo=timezone.utc),
datetime(2026, 9, 25, tzinfo=timezone.utc),
source="breeze", exchange="MCX",
)
# 3) Commodity / Equity Options
contract = OptionContract(
underlying="CRUDEOIL",
expiry=date(2026, 10, 16),
strike=Decimal("5600"),
option_type=OptionType.CALL,
exchange="MCX",
)
bars = client.derivatives.get_ohlcv(contract, Interval.M5, source="breeze")
3. Portfolio & Trading
from bandl.models.account.types import OrderSide, OrderType
from bandl.models.trading import OrderRequest, ProductType
# Demat holdings, cash balances, and margin info
holdings = client.portfolio.get_holdings(source="breeze")
balances = client.portfolio.get_balances(source="breeze")
margin = client.portfolio.get_margin(source="breeze")
# Place a regular order
ack = client.trade.place_order(
OrderRequest(
symbol="RELIANCE",
exchange="NSE",
side=OrderSide.BUY,
order_type=OrderType.LIMIT,
quantity=Decimal(10),
price=Decimal("2450.00"),
product=ProductType.DELIVERY,
),
source="breeze",
)
print("Order placed ID:", ack.order_id)
Typed bars instead of pandas
from bandl import OHLCV
bars: list[OHLCV] = client.crypto.get_ohlcv("BTCUSDT", Interval.H1)
bars[-1].close # Decimal — no float rounding
bars[-1].timestamp # tz-aware UTC datetime
List tradable symbols
client.list_symbols(source="binance", search="BTC", limit=20)
client.list_symbols(source="zerodha", exchange="NSE",
instrument_types=("EQ",), search="RELI", limit=10)
Intervals & timezones
One enum maps to every provider's native interval. Timestamps come back UTC.
from bandl import Interval
Interval.M1, Interval.M5, Interval.H1, Interval.D1 # 1m / 5m / 1h / 1d
df["timestamp"] = df["timestamp"].dt.tz_convert("Asia/Kolkata") # → IST for display
Configuration
from bandl import BandlConfig, ProviderSettings
config = BandlConfig(
providers={
"zerodha": ProviderSettings(api_key="...", access_token="..."),
"dhan": ProviderSettings(api_key="client_id", access_token="jwt"),
},
timeout_seconds=30,
default_crypto_provider="binance", # client.crypto default
default_equity_provider="zerodha", # client.equity default
default_derivatives_provider="dhan", # client.derivatives default
)
| Provider | api_key |
access_token |
Notes |
|---|---|---|---|
zerodha |
Kite API key | daily token | Tokens expire daily — regenerate after login. 403 ⇒ expired/wrong token or no historical-API access. |
dhan |
client id | JWT | JWT generated in the Dhan web/app. Expired contracts leave the scrip master — fetch by instrument_id. |
binance / coindcx |
— | — | Public OHLCV needs no keys. |
Binance HTTP 451? Binance blocks some regions/cloud IPs (US, Colab). Use
source="coindcx"— same symbols, no key — or setdefault_crypto_provider="coindcx".CoinDCX empty
DataFrame? Its public feed can lag by months. Astart/endentirely after the feed raisesDataNotAvailableErrorwith the available span; pick an overlapping window.
More
Account history — orders, fills & PnL via client.account
fills = client.account.get_fills(start, end, source="coindcx")
pnl = client.account.get_pnl(start, end, source="zerodha", prefer="auto")
bundle = client.account.export_analysis_bundle(start, end)
Full guide: docs/ACCOUNT_HISTORY.md.
Futures 24h leaders — rolling ticker stats
from bandl import AssetType
tickers = client.crypto.get_24hr_tickers(source="coindcx", asset_type=AssetType.CRYPTO_PERP)
Runnable demos
cp examples/.env.example .env # add ZERODHA_* / DHAN_* to test authed providers
python examples/main.py
python examples/dhan_options.py
python examples/futures_24hr_leaders.py --source coindcx
For AI agents
AGENTS.md is a purpose-built reference (provider matrix, recipes, errors) for LLM coding tools. It is not shipped in the PyPI wheel — point your agent at the GitHub link:
https://github.com/stockalgo/bandl/blob/master/AGENTS.md
Pin a tag (e.g. .../blob/v0.4.0/AGENTS.md) for a fixed version. See
agents/README.md.
Docs & development
- docs/BANDL.md — layout & design notes
- docs/ACCOUNT_HISTORY.md — account facet
- docs/EXECUTION_MIGRATION.md — live execution & recovery guide
- docs/PROVIDER_AUTHOR_GUIDE.md — building execution adapters
- CONTRIBUTING.md — tests, Ruff, pull requests
- SECURITY.md — reporting vulnerabilities
pytest tests/bandl/
ruff check lib/bandl tests/bandl
Roadmap
- Live streams / WebSockets
- More brokers & deeper commodity history
- Richer
SymbolInfoand fundamentals
Contributing
PRs welcome — read CONTRIBUTING.md and CODE_OF_CONDUCT.md first.
License
Release files for bandl 0.8.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bandl-0.8.0.tar.gz | 85.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bandl-0.8.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 193.9 kB
Release files / bandl-0.8.0.tar.gz
| Download URL | bandl-0.8.0.tar.gz |
|---|---|
| Size | 85.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ab59176059876cf5f2b2c5b3d9f4b2f1ee68f357ca4d57dda7623866d98a798e
|
|
BLAKE2b-256 checksum How to use checksums |
663ded456ae5e9b47cd5260d1054c1c3a196190a3f82a50080f62ce45cac28b1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.
Transparency logRelease files / bandl-0.8.0-py3-none-any.whl
| Download URL | bandl-0.8.0-py3-none-any.whl |
|---|---|
| Size | 108.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
25d7d6b2dffa79ead4c57212ca29d5887abe871e9c9c529a578062d3495744f5
|
|
BLAKE2b-256 checksum How to use checksums |
04c01466d8c5dfd55b4d4909148a66ccde505f53e279f5db275bcacdc290b25e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.
Transparency log