bdshare
bdshare is a Python library for fetching live and historical market data from the Dhaka Stock Exchange (DSE). It handles scraping, retries, caching, and rate limiting so you can focus on your analysis.
Table of Contents
- Installation
- Quick Start
- Core Concepts
- Usage Guide
- OOP Client (BDShare)
- Error Handling
- API Reference
- Examples
- Using bdshare with AI Agents (MCP Server)
- Contributing
- Roadmap
- Disclaimer
Installation
Requirements: Python 3.7+
pip install bdshare
Install from source (latest development version):
pip install -U git+https://github.com/rochi88/bdshare.git
Dependencies installed automatically: pandas, requests, beautifulsoup4, lxml
Quick Start
from bdshare import get_current_trade_data, get_historical_data
# Live prices for all instruments
df = get_current_trade_data()
print(df.head())
# Historical data for a specific symbol
df = get_historical_data('2024-01-01', '2024-01-31', 'GP')
print(df.head())
Or use the object-oriented client:
from bdshare import BDShare
with BDShare() as bd:
print(bd.get_market_summary())
print(bd.get_current_trades('ACI'))
Core Concepts
| Concept | Details |
|---|---|
| Retries | All network calls retry up to 3 times with exponential back-off |
| Fallback URL | Every request has a primary and an alternate DSE endpoint |
| Caching | The BDShare client caches responses automatically (configurable TTL) |
| Rate limiting | Built-in sliding-window limiter (5 calls/second) prevents being blocked |
| Errors | All failures raise BDShareError — never silent |
Usage Guide
Live Trading Data
from bdshare import get_current_trade_data, get_dsex_data, get_current_trading_code
# All instruments — returns columns: symbol, ltp, high, low, close, ycp, change, trade, value, volume
df = get_current_trade_data()
# Single instrument (case-insensitive)
df = get_current_trade_data('GP')
# DSEX index entries
df = get_dsex_data()
# Just the list of tradeable symbols
codes = get_current_trading_code()
print(codes['symbol'].tolist())
Historical Data
from bdshare import get_historical_data, get_basic_historical_data, get_close_price_data
import datetime as dt
start = '2024-01-01'
end = '2024-03-31'
# Full historical data (ltp, open, high, low, close, volume, trade, value…)
# Indexed by date, sorted newest-first
df = get_historical_data(start, end, 'ACI')
# Simplified OHLCV — sorted oldest-first, ready for TA libraries
df = get_basic_historical_data(start, end, 'ACI')
# Set date as index explicitly
df = get_basic_historical_data(start, end, 'ACI', index='date')
# Rolling 2-year window
end = dt.date.today()
start = end - dt.timedelta(days=2 * 365)
df = get_basic_historical_data(str(start), str(end), 'GP')
# Close prices only
df = get_close_price_data(start, end, 'ACI')
Column order note:
get_basic_historical_dataintentionally returns OHLCV in standard order (open,high,low,close,volume) to be compatible with libraries liketa,pandas-ta, andbacktrader.Deprecated aliases:
get_hist_data()andget_basic_hist_data()still work but emit aDeprecationWarning— migrate toget_historical_data()/get_basic_historical_data().
Market & Index Data
from bdshare import (
get_market_status,
get_market_info,
get_market_info_more_data,
get_market_depth_data,
get_latest_pe,
get_top_ten_gainers_losers,
get_top_twenty_shares,
get_company_info,
)
# Current market status: Open, Closed, Holiday, etc.
status = get_market_status()
# Last 30 days of market summary (DSEX, DSES, DS30, DGEN, volumes, market cap)
df = get_market_info()
# Historical market summary between two dates
df = get_market_info_more_data('2024-01-01', '2024-03-31')
# Order book (buy/sell depth) for a symbol
df = get_market_depth_data('ACI')
# P/E ratios for all listed companies
df = get_latest_pe()
# Top 10 gainers and losers (adjust limit as needed)
df = get_top_ten_gainers_losers(limit=10)
# Top 20 shares by traded volume (adjust limit as needed)
df = get_top_twenty_shares(limit=20)
# Detailed company profile
tables = get_company_info('GP')
News & Announcements
from bdshare import get_news, get_agm_news, get_all_news
# Unified dispatcher — news_type: 'all' | 'agm' | 'corporate' | 'psn'
df = get_news(news_type='all')
df = get_news(news_type='agm')
df = get_news(news_type='corporate', code='GP')
df = get_news(news_type='psn', code='ACI') # price-sensitive news
# Direct function calls
df = get_agm_news() # AGM / dividend declarations
df = get_all_news(code='BEXIMCO') # All news for one symbol
df = get_all_news('2024-01-01', '2024-03-31', 'GP') # Filtered by date + symbol
Saving to CSV
from bdshare import get_basic_historical_data, Store
import datetime as dt
end = dt.date.today()
start = end - dt.timedelta(days=365)
df = get_basic_historical_data(str(start), str(end), 'GP')
Store(df).save() # saves to current directory as a CSV
OOP Client (BDShare)
The BDShare class wraps all functions with automatic caching and rate limiting.
from bdshare import BDShare
bd = BDShare(cache_enabled=True) # cache_enabled=True is the default
Context manager (auto-cleans cache and session)
with BDShare() as bd:
data = bd.get_current_trades('GP')
Market methods
bd.get_market_summary() # DSEX/DSES/DS30 indices + stats (1-min TTL)
bd.get_company_profile('ACI') # Company profile (1-hr TTL)
bd.get_latest_pe_ratios() # All P/E ratios (1-hr TTL)
bd.get_top_movers(limit=10) # Top gainers/losers (5-min TTL)
get_market_status() and get_top_twenty_shares() don't have BDShare wrapper methods yet —
call the module-level functions directly (see API Reference).
Trading methods
bd.get_current_trades() # All live prices (30-sec TTL)
bd.get_current_trades('GP') # Single symbol
bd.get_dsex_index() # DSEX index entries (1-min TTL)
bd.get_trading_codes() # All tradeable symbols (24-hr TTL)
bd.get_historical_data('GP', '2024-01-01', '2024-03-31') # OHLCV history
News methods
bd.get_news(news_type='all') # All news (5-min TTL)
bd.get_news(news_type='corporate', code='GP')
bd.get_news(news_type='psn') # Price-sensitive news
Utility methods
bd.clear_cache() # Flush all cached data
bd.configure(proxy_url='http://proxy:8080')
print(bd.version) # Package version string
Error Handling
All failures raise BDShareError. Never catch bare Exception — you'll miss bugs.
from bdshare import BDShare, BDShareError
bd = BDShare()
try:
df = bd.get_historical_data('INVALID', '2024-01-01', '2024-01-31')
except BDShareError as e:
print(f"DSE error: {e}")
# safe fallback logic here
Common causes of BDShareError:
- Symbol not found in the response table
- DSE site returned a non-200 status after all retries
- Table structure changed on the DSE page (report as a bug)
- Network timeout
API Reference
Trading Functions
| Function | Parameters | Returns | Description |
|---|---|---|---|
get_current_trade_data(symbol?) |
symbol: str |
DataFrame | Live prices (all or one symbol) |
get_dsex_data(symbol?) |
symbol: str |
DataFrame | DSEX index entries |
get_current_trading_code() |
— | DataFrame | All tradeable symbols |
get_historical_data(start, end, code?) |
str, str, str |
DataFrame | Full historical OHLCV |
get_basic_historical_data(start, end, code?, index?) |
str, str, str, str |
DataFrame | Simplified OHLCV (TA-ready) |
get_close_price_data(start, end, code?) |
str, str, str |
DataFrame | Close + prior close |
get_last_trade_price_data() |
— | DataFrame | Last trade from DSE text file |
Deprecated aliases (removed in 2.0.0): get_hist_data() → get_historical_data(),
get_basic_hist_data() → get_basic_historical_data().
Market Functions
| Function | Parameters | Returns | Description |
|---|---|---|---|
get_market_status() |
— | str | Current market status (Open, Closed, Holiday, etc.) |
get_market_info() |
— | DataFrame | 30-day market summary |
get_market_info_more_data(start, end) |
str, str |
DataFrame | Historical market summary |
get_market_depth_data(symbol) |
str |
DataFrame | Order book (buy/sell depth) |
get_latest_pe() |
— | DataFrame | P/E ratios for all companies |
get_company_info(symbol) |
str |
list[DataFrame] | Detailed company tables |
get_top_ten_gainers_losers(limit?) |
int (default 10) |
DataFrame | Top movers by price change |
get_top_twenty_shares(limit?) |
int (default 20) |
DataFrame | Top shares by traded volume |
News Functions
| Function | Parameters | Returns | Description |
|---|---|---|---|
get_news(news_type?, code?) |
str, str |
DataFrame | Unified news dispatcher |
get_agm_news() |
— | DataFrame | AGM / dividend declarations |
get_all_news(start?, end?, code?) |
str, str, str |
DataFrame | All DSE news |
get_corporate_announcements(code?) |
str |
DataFrame | Corporate actions |
get_price_sensitive_news(code?) |
str |
DataFrame | Price-sensitive news |
get_news news_type values
| Value | Equivalent direct function |
|---|---|
'all' |
get_all_news() |
'agm' |
get_agm_news() |
'corporate' |
get_corporate_announcements() |
'psn' |
get_price_sensitive_news() |
Examples
Stock performance summary
import datetime as dt
from bdshare import BDShare, BDShareError
def summarize(symbol: str, days: int = 30) -> dict:
end = dt.date.today()
start = end - dt.timedelta(days=days)
with BDShare() as bd:
try:
df = bd.get_historical_data(symbol, str(start), str(end))
except BDShareError as e:
print(f"Could not fetch data: {e}")
return {}
return {
'symbol': symbol,
'current': df['close'].iloc[0],
'period_high': df['high'].max(),
'period_low': df['low'].min(),
'avg_volume': df['volume'].mean(),
'change_pct': (df['close'].iloc[0] - df['close'].iloc[-1])
/ df['close'].iloc[-1] * 100,
}
result = summarize('GP', days=30)
print(f"{result['symbol']}: {result['change_pct']:.2f}% over 30 days")
Simple portfolio tracker
from bdshare import BDShare, BDShareError
PORTFOLIO = {
'GP': {'qty': 100, 'cost': 450.50},
'ACI': {'qty': 50, 'cost': 225.75},
'BEXIMCO': {'qty': 200, 'cost': 125.25},
}
with BDShare() as bd:
total_cost = total_value = 0
for symbol, pos in PORTFOLIO.items():
try:
row = bd.get_current_trades(symbol).iloc[0]
price = row['ltp']
market_value = pos['qty'] * price
cost = pos['qty'] * pos['cost']
pnl = market_value - cost
print(f"{symbol:10s} price={price:8.2f} P&L={pnl:+10.2f}")
total_cost += cost
total_value += market_value
except BDShareError as e:
print(f"{symbol}: fetch error — {e}")
print(f"\nPortfolio P&L: {total_value - total_cost:+.2f} "
f"({(total_value/total_cost - 1)*100:+.2f}%)")
Fetch and screen top gainers above 5 %
from bdshare import get_top_ten_gainers_losers
df = get_top_ten_gainers_losers(limit=20)
big_movers = df[df['change'] > 5]
print(big_movers[['symbol', 'close', 'change']])
Using bdshare with AI Agents (MCP Server)
bdshare ships an MCP server so AI agents running in another program — Claude Desktop, Claude Code, or any other MCP-compatible client — can call live DSE data as tools, without you writing any glue code.
Install
pip install "bdshare[mcp]"
Run
bdshare-mcp
# or
python -m bdshare.mcp_server
By default it speaks MCP over stdio, which is what desktop/CLI agent clients expect.
Connect it to a client
Claude Code:
claude mcp add bdshare -- bdshare-mcp
Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"bdshare": {
"command": "bdshare-mcp"
}
}
}
Any other MCP client is configured the same way — point it at the bdshare-mcp command
(or python -m bdshare.mcp_server), stdio transport.
What the agent gets
15 tools covering the same data this README documents — market_status,
market_summary, market_summary_range, market_depth, latest_pe_ratios,
top_ten_gainers_losers, top_twenty_shares, company_info, current_trades,
dsex_index, trading_codes, historical_data, basic_historical_data, news,
agm_news. Each tool returns JSON — DataFrames are converted to lists of row
records — and a BDShareError (bad symbol, DSE outage, parse failure) surfaces as a
clean tool error message instead of a raw traceback.
Notes for agent use
- No caching in the MCP server itself. Every tool call scrapes dsebd.org live. If
your agent calls the same tool repeatedly in one turn (e.g. checking a price several
times), consider fronting it with the
BDShareclient's caching in a custom wrapper — the bundled server intentionally stays stateless and simple. - Rate limits are the agent's responsibility. The
BDShareOOP client has a built-in 5 calls/second limiter; the MCP tools call the plain module-level functions, which don't. Avoid tight loops of tool calls. - Source:
bdshare/mcp_server.py— a plainFastMCPserver, easy to fork if you want a different tool surface.
Contributing
Contributions are welcome! To get started:
git clone https://github.com/rochi88/bdshare.git
cd bdshare
pip install -e ".[dev]"
pytest
Please open an issue before submitting a pull request for significant changes. See CONTRIBUTING.md for the full guide.
Roadmap
- Chittagong Stock Exchange (CSE) support
- WebSocket streaming for real-time ticks
- Built-in technical indicators (
taintegration) - Portfolio management helpers
- Docker demo examples
- Shared session with exponential back-off
-
lxml-based fast parsing -
BDShareErrorfor clean error handling - Unified
get_news()dispatcher - Rate limiter and response caching
- MCP server for AI agent integration
Support
- Docs: bdshare.readthedocs.io
- Bugs / Features: GitHub Issues
- Discussion: GitHub Discussions
License
MIT — see LICENSE for details.
Disclaimer
bdshare is intended for educational and research use. Always respect DSE's terms of service. The authors are not responsible for financial decisions made using this library.
Change log
All notable changes to bdshare are documented here.
Format follows Keep a Changelog. Versioning follows Semantic Versioning.
[1.2.2] - 2026-07-22
Added
- Optional
as_polars=Trueparameter across all data-fetching functions to returnpolars.DataFrameinstead ofpandas.DataFrame(requirespip install bdshare[polars]) get_market_status()— current market status (Open, Closed, Holiday, etc.)get_top_twenty_shares()— top twenty shares by traded volume- MCP (Model Context Protocol) server (
bdshare/mcp_server.py) exposing 15 tools so AI agents (Claude Desktop, Claude Code, etc.) can call live DSE data directly; install withpip install bdshare[mcp], run withbdshare-mcp
Changed
- Renamed
get_top_gainers_losers()→get_top_ten_gainers_losers(); returned columns changed fromsymbol, ltp, changetosymbol, close, high, low, ycp, change
Removed
get_sector_performance()(module function andBDShare.get_sector_performance()) — no aliased replacement; useget_top_twenty_shares()instead
Fixed
- Bundled the missing Sectigo DV R36 intermediate certificate so requests to dsebd.org verify correctly instead of relying on an incomplete chain
- Corrected
DSE_ALT_URLfallback domain todse.com.bd get_corporate_announcements()andget_price_sensitive_news()always raisedBDShareError— the row parser assumed each news item was one<tr>with 4<td>s, but DSE renders each field (Trading Code:,News Title:,News:,Post Date:) as its own<th>/<td>row pairas_polars=Truecould crash withunexpected value while building Series of type String; found value of type Float64: NaNon tables mixing strings and missing values (e.g.get_company_info()); NaN now converts to a proper polars null instead- Corrected
usage.rst/README documentation that hadget_historical_data()/get_basic_historical_data()mislabeled as the deprecated aliases —get_hist_data()/get_basic_hist_data()are the deprecated ones
[1.2.1] - 2026-02-22
Added
- Added code filter on market index
[1.2.0] - 2026-02-21
Changed
- Updated
[1.1.6] - 2026-02-20
Changed
- Updated
readthedocsstructure
[1.1.5] - 2026-02-20
Changed
- Renamed
get_hist_data()→get_historical_data()(improved readability) - Renamed
get_basic_hist_data()→get_basic_historical_data()(improved readability) - Renamed
get_market_inf()→get_market_info()(improved readability) - Renamed
get_market_inf_more_data()→get_market_info_more_data()(improved readability) - Renamed
get_company_inf()→get_company_info()(improved readability)
Deprecated
get_hist_data()— still callable but emitsDeprecationWarning; will be removed in 2.0.0get_basic_hist_data()— still callable but emitsDeprecationWarning; will be removed in 2.0.0get_market_inf()— still callable but emitsDeprecationWarning; will be removed in 2.0.0get_market_inf_more_data()— still callable but emitsDeprecationWarning; will be removed in 2.0.0get_company_inf()— still callable but emitsDeprecationWarning; will be removed in 2.0.0
Added
BDShareErrorcustom exception — all network and scraping failures now raise this instead of silently printing and returningNone- Shared
requests.Session(_session) across all modules — reuses TCP connections for significantly faster repeated calls - Exponential back-off retry logic in
safe_get()— pauses 0.2 s → 0.4 s → 0.8 s between attempts before raisingBDShareError - Fallback URL support in
safe_get()— primary and alternate DSE endpoints tried within the same retry attempt lxml-based HTML parsing in_fetch_table()withhtml.parserfallback — replaceshtml5lib(~10× faster)_safe_num()helper — all scraped values now returned as typed numerics (float/int) instead of raw strings_parse_trade_rows()and_filter_symbol()internal helpers intrading.py— eliminate duplicated parsing logic betweenget_current_trade_data()andget_dsex_data()get_news()unified dispatcher — acceptsnews_typeof'all','agm','corporate', or'psn'get_corporate_announcements()andget_price_sensitive_news()— previously missing functions now fully implemented- Column count guards (
len(cols) < N) across all table parsers — malformed rows are skipped rather than raisingIndexError - Backward-compatibility aliases for all renamed functions with
DeprecationWarning - Type hints throughout all public functions
- Comprehensive docstrings with parameter and return documentation
Fixed
get_agm_news(): corrected field name typoagmData→agmDateget_agm_news(): corrected field name typovanue→venueget_market_depth_data(): no longer creates a newrequests.Sessionon every retry iterationget_basic_hist_data(): redundant doublesort_index()call removedget_hist_data()andget_close_price_data(): no longer silently returnNoneon empty resultsRateLimiterin__init__.py: switched fromtime.time()totime.monotonic()for reliable elapsed-time measurement
Removed
html5libas the default parser — replaced bylxmlwithhtml.parserfallback- Silent
print(e)error handling — all error paths now raiseBDShareError - Dead
timeoutparameter fromBDShare.configure()— it had no effect
[1.1.4] - 2025-09-16
Added
- Enhanced error handling and robustness across all functions
- Improved parameter handling for news functions
- Better file path resolution for utility functions
- Comprehensive fallback mechanisms for network issues
Changed
- Fixed get_all_news() function to support date range parameters as documented
- Enhanced market info functions with better error handling
- Improved Store utility with proper file saving mechanism
- Fixed Tickers utility with correct file path resolution
Fixed
- All major function issues identified in testing (18/18 functions now working)
- Parameter signature mismatches in news functions
- HTML parsing errors in market data functions
- File saving issues in Store utility
- Missing tickers.json file dependency
[1.1.2] - 2024-12-31
Added
- n/a
Changed
- update tests
Fixed
- n/a
[1.1.1] - 2024-12-31
Added
- n/a
Changed
- update runner
Fixed
- n/a
[1.1.0] - 2024-12-31
Added
- new function for getting company info
Changed
- n/a
Fixed
- n/a
[1.0.4] - 2024-12-30
Added
- n/a
Changed
- changed lint
Fixed
- fixed typo
[1.0.3] - 2024-07-29
Added
- n/a
Changed
- n/a
Fixed
- check fix for latest P/E url [#6]
[1.0.2] - 2024-07-29
Added
- n/a
Changed
- n/a
Fixed
- fixed latest P/E url [#6]
[1.0.0] - 2024-03-04
Added
- Updated docs
Changed
- n/a
[0.7.2] - 2024-03-04
Added
- Updated docs
Changed
- n/a
[0.7.1] - 2024-03-04
Added
- n/a
Changed
- fixed market depth data api
[0.7.0] - 2024-03-04
Added
- n/a
Changed
- n/a
[0.6.0] - 2024-03-03
Added
- n/a
Changed
- n/a
[0.5.1] - 2024-02-29
Added
- n/a
Changed
- n/a
[0.5.0] - 2024-02-29
Added
- fixed store datafrave to csv file method
Changed
- n/a
[0.4.0] - 2023-03-12
Added
- n/a
Changed
- changed package manager
[0.3.2] - 2022-10-10
Added
- n/a
Changed
- n/a
[0.3.1] - 2022-06-15
Added
- n/a
Changed
- n/a
[0.2.1] - 2021-08-01
Added
Changed
get_current_trading_code()
[0.2.0] - 2021-06-01
Added
- added get_market_depth_data
- added get_dsex_data
- added 'dse.com.bd' as redundant
Changed
- Changed documentation
- changed get_agm_news
- changed get_all_news
[0.1.4] - 2020-08-22
Added
- added get_market_inf_more_data
Changed
- Changed documentation
[0.1.3] - 2020-08-20
Added
- html5lib
- added get params
Changed
- post request to get
[0.1.2] - 2020-05-21
Added
- modified index declaration
[0.1.1] - 2020-05-20
Added
- modified index declaration
[0.1.0] - 2020-04-08
Added
- added git tag
VERSION.txt
Changed
setup.pyHISTORY.mdtoCHANGELOG.md
[0.0.1] - 2020-04-06
Added
get_hist_data(), get_current_trade_data()HISTORY.md
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