powernse 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- powernse-0.1.0/LICENSE +21 -0
- powernse-0.1.0/PKG-INFO +175 -0
- powernse-0.1.0/README.md +142 -0
- powernse-0.1.0/pyproject.toml +87 -0
- powernse-0.1.0/pyproject.toml.orig +73 -0
- powernse-0.1.0/src/powernse/__init__.py +60 -0
- powernse-0.1.0/src/powernse/__main__.py +6 -0
- powernse-0.1.0/src/powernse/adjust.py +100 -0
- powernse-0.1.0/src/powernse/archive/__init__.py +39 -0
- powernse-0.1.0/src/powernse/archive/layout.py +106 -0
- powernse-0.1.0/src/powernse/archive/manifest.py +68 -0
- powernse-0.1.0/src/powernse/archive/zipcsv.py +32 -0
- powernse-0.1.0/src/powernse/bundle.py +148 -0
- powernse-0.1.0/src/powernse/calendar.py +69 -0
- powernse-0.1.0/src/powernse/cli/__init__.py +5 -0
- powernse-0.1.0/src/powernse/cli/app.py +76 -0
- powernse-0.1.0/src/powernse/cli/bundle_cmd.py +41 -0
- powernse-0.1.0/src/powernse/cli/common.py +58 -0
- powernse-0.1.0/src/powernse/cli/dated.py +70 -0
- powernse-0.1.0/src/powernse/cli/read.py +159 -0
- powernse-0.1.0/src/powernse/cli/snapshots.py +126 -0
- powernse-0.1.0/src/powernse/constants.py +27 -0
- powernse-0.1.0/src/powernse/data.py +430 -0
- powernse-0.1.0/src/powernse/downloaders/__init__.py +85 -0
- powernse-0.1.0/src/powernse/downloaders/base.py +77 -0
- powernse-0.1.0/src/powernse/downloaders/bhavcopy.py +108 -0
- powernse-0.1.0/src/powernse/downloaders/corporate_actions.py +159 -0
- powernse-0.1.0/src/powernse/downloaders/dated.py +95 -0
- powernse-0.1.0/src/powernse/downloaders/deals.py +110 -0
- powernse-0.1.0/src/powernse/downloaders/fo_bhavcopy.py +93 -0
- powernse-0.1.0/src/powernse/downloaders/full_bhavcopy.py +77 -0
- powernse-0.1.0/src/powernse/downloaders/index_closes.py +77 -0
- powernse-0.1.0/src/powernse/downloaders/index_constituents.py +150 -0
- powernse-0.1.0/src/powernse/downloaders/resume.py +55 -0
- powernse-0.1.0/src/powernse/errors.py +17 -0
- powernse-0.1.0/src/powernse/http/__init__.py +5 -0
- powernse-0.1.0/src/powernse/http/client.py +151 -0
- powernse-0.1.0/src/powernse/loaders.py +8 -0
- powernse-0.1.0/src/powernse/parsers/__init__.py +5 -0
- powernse-0.1.0/src/powernse/parsers/rows.py +185 -0
- powernse-0.1.0/src/powernse/py.typed +0 -0
- powernse-0.1.0/src/powernse/settings.py +89 -0
- powernse-0.1.0/src/powernse/types.py +88 -0
powernse-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PowerNSE contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
powernse-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: powernse
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Download and use official NSE India equity archives via CLI and Python
|
|
5
|
+
Keywords: nse,india,bhavcopy,equities,market-data,download
|
|
6
|
+
Author: Vishal Mishra
|
|
7
|
+
Author-email: Vishal Mishra <vishal.k.mishra2@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Dist: exchange-calendars>=4.5
|
|
19
|
+
Requires-Dist: filelock>=3.32
|
|
20
|
+
Requires-Dist: pydantic-settings>=2.15
|
|
21
|
+
Requires-Dist: requests>=2.32
|
|
22
|
+
Requires-Dist: tenacity>=9.0
|
|
23
|
+
Requires-Dist: typer>=0.27
|
|
24
|
+
Requires-Dist: pandas>=2.2 ; extra == 'pandas'
|
|
25
|
+
Requires-Python: >=3.13
|
|
26
|
+
Project-URL: Homepage, https://inquilabee.github.io/powernse/
|
|
27
|
+
Project-URL: Documentation, https://inquilabee.github.io/powernse/
|
|
28
|
+
Project-URL: Repository, https://github.com/inquilabee/powernse
|
|
29
|
+
Project-URL: Issues, https://github.com/inquilabee/powernse/issues
|
|
30
|
+
Project-URL: Changelog, https://github.com/inquilabee/powernse/blob/main/CHANGELOG.md
|
|
31
|
+
Provides-Extra: pandas
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
<p align="center">
|
|
35
|
+
<a href="https://pypi.org/project/powernse/"><img src="https://img.shields.io/pypi/v/powernse.svg" alt="PyPI version"/></a>
|
|
36
|
+
<a href="https://pypi.org/project/powernse/"><img src="https://img.shields.io/pypi/pyversions/powernse.svg" alt="Python versions"/></a>
|
|
37
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT"/></a>
|
|
38
|
+
<a href="https://inquilabee.github.io/powernse/"><img src="https://img.shields.io/badge/docs-GitHub%20Pages-blue" alt="Documentation"/></a>
|
|
39
|
+
</p>
|
|
40
|
+
|
|
41
|
+
<p align="center">
|
|
42
|
+
<strong>Official NSE India archives on disk. One CLI. A small Python API.</strong><br/>
|
|
43
|
+
Bhavcopy, F&O, indexes, deals, corporate actions — downloaded from the exchange,
|
|
44
|
+
not scraped from a third-party dump.
|
|
45
|
+
</p>
|
|
46
|
+
|
|
47
|
+
<p align="center">
|
|
48
|
+
<a href="https://inquilabee.github.io/powernse/">Docs</a> ·
|
|
49
|
+
<a href="https://inquilabee.github.io/powernse/user/quickstart/">Quick start</a> ·
|
|
50
|
+
<a href="https://pypi.org/project/powernse/">PyPI</a> ·
|
|
51
|
+
<a href="CHANGELOG.md">Changelog</a>
|
|
52
|
+
</p>
|
|
53
|
+
|
|
54
|
+
______________________________________________________________________
|
|
55
|
+
|
|
56
|
+
NSE publishes end-of-day files. The URLs move, the session cookie is picky, and
|
|
57
|
+
nobody wants to re-learn the layout every quarter. **PowerNSE** stages those
|
|
58
|
+
archives under a local `nse-data/` tree, keeps a download manifest, and lets you
|
|
59
|
+
query OHLC from the CLI or from Python.
|
|
60
|
+
|
|
61
|
+
Need a ready-made tree? `powernse fetch-bundle` pulls the tracked `nse-data/` from
|
|
62
|
+
this GitHub repo. Prefer building it yourself? Point the downloaders at NSE.
|
|
63
|
+
|
|
64
|
+
Python **3.13+**.
|
|
65
|
+
|
|
66
|
+
## Install
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install powernse
|
|
70
|
+
pip install 'powernse[pandas]' # optional DataFrame helpers
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Quick start
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
powernse bhavcopy --resume
|
|
77
|
+
powernse fo-bhavcopy --resume --days 30
|
|
78
|
+
powernse index-closes --from 2024-08-01 --to 2024-08-05
|
|
79
|
+
powernse fetch-bundle --force # optional: GitHub-hosted archive
|
|
80
|
+
powernse status
|
|
81
|
+
powernse ohlc RELIANCE --from 2024-08-01 --to 2024-08-05
|
|
82
|
+
powernse doctor
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from datetime import date
|
|
87
|
+
from powernse import BhavcopyDownloader, NSEData
|
|
88
|
+
|
|
89
|
+
BhavcopyDownloader("./nse-data").download_range(date(2024, 8, 1), date(2024, 8, 5))
|
|
90
|
+
data = NSEData("./nse-data")
|
|
91
|
+
bars = data.ohlc("RELIANCE", from_date=date(2024, 8, 1), to_date=date(2024, 8, 5))
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Full walkthrough: [docs site quickstart](https://inquilabee.github.io/powernse/user/quickstart/).
|
|
95
|
+
|
|
96
|
+
## What you get
|
|
97
|
+
|
|
98
|
+
| Surface | Covers |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| Cash / F&O / full bhavcopy | Daily equity and derivatives archives |
|
|
101
|
+
| Index closes & constituents | Index levels and membership snapshots |
|
|
102
|
+
| Bulk / block deals, F&O ban | As-of snapshots |
|
|
103
|
+
| Corporate actions | JSON for adjustment helpers |
|
|
104
|
+
| `NSEData` / CLI | OHLC, coverage gaps, inventory, doctor |
|
|
105
|
+
| `fetch-bundle` | Zip extract of this repo's `nse-data/` |
|
|
106
|
+
|
|
107
|
+
Trading days use XBOM sessions when `exchange-calendars` covers the window
|
|
108
|
+
(from 2006-08-16). Earlier dates fall back to weekdays.
|
|
109
|
+
|
|
110
|
+
## Archive layout
|
|
111
|
+
|
|
112
|
+
```text
|
|
113
|
+
nse-data/
|
|
114
|
+
raw/bhavcopy/YYYY/YYYY-MM-DD.csv
|
|
115
|
+
raw/fo_bhavcopy/…
|
|
116
|
+
raw/full_bhavcopy/…
|
|
117
|
+
raw/index_closes/…
|
|
118
|
+
raw/bulk_deals/… raw/block_deals/… raw/fo_secban/…
|
|
119
|
+
raw/corporate_actions/YYYY/YYYY-MM-DD.json
|
|
120
|
+
raw/index_constituents/YYYY/YYYY-MM-DD_<index>.json
|
|
121
|
+
manifest/downloads.jsonl
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Etiquette
|
|
125
|
+
|
|
126
|
+
Requests are throttled and an NSE session cookie is primed first. Keep date
|
|
127
|
+
ranges modest; `--skip-existing` is on by default. Exchange URLs can change
|
|
128
|
+
without notice.
|
|
129
|
+
|
|
130
|
+
This project does **not** download or ingest Kaggle (or other third-party) dumps.
|
|
131
|
+
If you only need rough historical OHLCV for experiments, fetch those yourself —
|
|
132
|
+
PowerNSE sticks to official archives.
|
|
133
|
+
|
|
134
|
+
## Disclaimer
|
|
135
|
+
|
|
136
|
+
PowerNSE is **not affiliated with, endorsed by, or connected to** the National
|
|
137
|
+
Stock Exchange of India (NSE) or any related entity. It is an unofficial tool
|
|
138
|
+
for **educational and research** use only — not financial advice, and not an
|
|
139
|
+
official data feed.
|
|
140
|
+
|
|
141
|
+
You are responsible for how you use the software and any data it retrieves,
|
|
142
|
+
including compliance with NSE terms of use. The author accepts **no liability**
|
|
143
|
+
for losses, damages, or other consequences arising from use of this package.
|
|
144
|
+
Use at your own risk.
|
|
145
|
+
|
|
146
|
+
Full text: [DISCLAIMER.md](DISCLAIMER.md).
|
|
147
|
+
|
|
148
|
+
## Documentation
|
|
149
|
+
|
|
150
|
+
| Guide | Link |
|
|
151
|
+
| --- | --- |
|
|
152
|
+
| [Install](https://inquilabee.github.io/powernse/user/install/) | pip / uv / clone |
|
|
153
|
+
| [Quick start](https://inquilabee.github.io/powernse/user/quickstart/) | Download + query |
|
|
154
|
+
| [Archives](https://inquilabee.github.io/powernse/user/download/archives/) | Every downloader |
|
|
155
|
+
| [NSEData](https://inquilabee.github.io/powernse/user/python/nsedata/) | Python API |
|
|
156
|
+
| [fetch-bundle](https://inquilabee.github.io/powernse/user/bundle/fetch-bundle/) | GitHub zip extract |
|
|
157
|
+
| [Disclaimer](https://inquilabee.github.io/powernse/user/disclaimer/) | Unofficial · educational use |
|
|
158
|
+
| [Publish](https://inquilabee.github.io/powernse/maintainer/publish/) | Maintainer release |
|
|
159
|
+
|
|
160
|
+
## Development
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
uv sync
|
|
164
|
+
make check # ShipGate
|
|
165
|
+
make format
|
|
166
|
+
make test
|
|
167
|
+
make build
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Local docs: `uv run --with mkdocs-material mkdocs serve`. Quality gates:
|
|
171
|
+
[ShipGate](https://inquilabee.github.io/shipgate/) (`suite: standard`).
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
MIT — see [LICENSE](LICENSE). Also read [DISCLAIMER.md](DISCLAIMER.md).
|
powernse-0.1.0/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://pypi.org/project/powernse/"><img src="https://img.shields.io/pypi/v/powernse.svg" alt="PyPI version"/></a>
|
|
3
|
+
<a href="https://pypi.org/project/powernse/"><img src="https://img.shields.io/pypi/pyversions/powernse.svg" alt="Python versions"/></a>
|
|
4
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT"/></a>
|
|
5
|
+
<a href="https://inquilabee.github.io/powernse/"><img src="https://img.shields.io/badge/docs-GitHub%20Pages-blue" alt="Documentation"/></a>
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<strong>Official NSE India archives on disk. One CLI. A small Python API.</strong><br/>
|
|
10
|
+
Bhavcopy, F&O, indexes, deals, corporate actions — downloaded from the exchange,
|
|
11
|
+
not scraped from a third-party dump.
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="https://inquilabee.github.io/powernse/">Docs</a> ·
|
|
16
|
+
<a href="https://inquilabee.github.io/powernse/user/quickstart/">Quick start</a> ·
|
|
17
|
+
<a href="https://pypi.org/project/powernse/">PyPI</a> ·
|
|
18
|
+
<a href="CHANGELOG.md">Changelog</a>
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
______________________________________________________________________
|
|
22
|
+
|
|
23
|
+
NSE publishes end-of-day files. The URLs move, the session cookie is picky, and
|
|
24
|
+
nobody wants to re-learn the layout every quarter. **PowerNSE** stages those
|
|
25
|
+
archives under a local `nse-data/` tree, keeps a download manifest, and lets you
|
|
26
|
+
query OHLC from the CLI or from Python.
|
|
27
|
+
|
|
28
|
+
Need a ready-made tree? `powernse fetch-bundle` pulls the tracked `nse-data/` from
|
|
29
|
+
this GitHub repo. Prefer building it yourself? Point the downloaders at NSE.
|
|
30
|
+
|
|
31
|
+
Python **3.13+**.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install powernse
|
|
37
|
+
pip install 'powernse[pandas]' # optional DataFrame helpers
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quick start
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
powernse bhavcopy --resume
|
|
44
|
+
powernse fo-bhavcopy --resume --days 30
|
|
45
|
+
powernse index-closes --from 2024-08-01 --to 2024-08-05
|
|
46
|
+
powernse fetch-bundle --force # optional: GitHub-hosted archive
|
|
47
|
+
powernse status
|
|
48
|
+
powernse ohlc RELIANCE --from 2024-08-01 --to 2024-08-05
|
|
49
|
+
powernse doctor
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from datetime import date
|
|
54
|
+
from powernse import BhavcopyDownloader, NSEData
|
|
55
|
+
|
|
56
|
+
BhavcopyDownloader("./nse-data").download_range(date(2024, 8, 1), date(2024, 8, 5))
|
|
57
|
+
data = NSEData("./nse-data")
|
|
58
|
+
bars = data.ohlc("RELIANCE", from_date=date(2024, 8, 1), to_date=date(2024, 8, 5))
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Full walkthrough: [docs site quickstart](https://inquilabee.github.io/powernse/user/quickstart/).
|
|
62
|
+
|
|
63
|
+
## What you get
|
|
64
|
+
|
|
65
|
+
| Surface | Covers |
|
|
66
|
+
| --- | --- |
|
|
67
|
+
| Cash / F&O / full bhavcopy | Daily equity and derivatives archives |
|
|
68
|
+
| Index closes & constituents | Index levels and membership snapshots |
|
|
69
|
+
| Bulk / block deals, F&O ban | As-of snapshots |
|
|
70
|
+
| Corporate actions | JSON for adjustment helpers |
|
|
71
|
+
| `NSEData` / CLI | OHLC, coverage gaps, inventory, doctor |
|
|
72
|
+
| `fetch-bundle` | Zip extract of this repo's `nse-data/` |
|
|
73
|
+
|
|
74
|
+
Trading days use XBOM sessions when `exchange-calendars` covers the window
|
|
75
|
+
(from 2006-08-16). Earlier dates fall back to weekdays.
|
|
76
|
+
|
|
77
|
+
## Archive layout
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
nse-data/
|
|
81
|
+
raw/bhavcopy/YYYY/YYYY-MM-DD.csv
|
|
82
|
+
raw/fo_bhavcopy/…
|
|
83
|
+
raw/full_bhavcopy/…
|
|
84
|
+
raw/index_closes/…
|
|
85
|
+
raw/bulk_deals/… raw/block_deals/… raw/fo_secban/…
|
|
86
|
+
raw/corporate_actions/YYYY/YYYY-MM-DD.json
|
|
87
|
+
raw/index_constituents/YYYY/YYYY-MM-DD_<index>.json
|
|
88
|
+
manifest/downloads.jsonl
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Etiquette
|
|
92
|
+
|
|
93
|
+
Requests are throttled and an NSE session cookie is primed first. Keep date
|
|
94
|
+
ranges modest; `--skip-existing` is on by default. Exchange URLs can change
|
|
95
|
+
without notice.
|
|
96
|
+
|
|
97
|
+
This project does **not** download or ingest Kaggle (or other third-party) dumps.
|
|
98
|
+
If you only need rough historical OHLCV for experiments, fetch those yourself —
|
|
99
|
+
PowerNSE sticks to official archives.
|
|
100
|
+
|
|
101
|
+
## Disclaimer
|
|
102
|
+
|
|
103
|
+
PowerNSE is **not affiliated with, endorsed by, or connected to** the National
|
|
104
|
+
Stock Exchange of India (NSE) or any related entity. It is an unofficial tool
|
|
105
|
+
for **educational and research** use only — not financial advice, and not an
|
|
106
|
+
official data feed.
|
|
107
|
+
|
|
108
|
+
You are responsible for how you use the software and any data it retrieves,
|
|
109
|
+
including compliance with NSE terms of use. The author accepts **no liability**
|
|
110
|
+
for losses, damages, or other consequences arising from use of this package.
|
|
111
|
+
Use at your own risk.
|
|
112
|
+
|
|
113
|
+
Full text: [DISCLAIMER.md](DISCLAIMER.md).
|
|
114
|
+
|
|
115
|
+
## Documentation
|
|
116
|
+
|
|
117
|
+
| Guide | Link |
|
|
118
|
+
| --- | --- |
|
|
119
|
+
| [Install](https://inquilabee.github.io/powernse/user/install/) | pip / uv / clone |
|
|
120
|
+
| [Quick start](https://inquilabee.github.io/powernse/user/quickstart/) | Download + query |
|
|
121
|
+
| [Archives](https://inquilabee.github.io/powernse/user/download/archives/) | Every downloader |
|
|
122
|
+
| [NSEData](https://inquilabee.github.io/powernse/user/python/nsedata/) | Python API |
|
|
123
|
+
| [fetch-bundle](https://inquilabee.github.io/powernse/user/bundle/fetch-bundle/) | GitHub zip extract |
|
|
124
|
+
| [Disclaimer](https://inquilabee.github.io/powernse/user/disclaimer/) | Unofficial · educational use |
|
|
125
|
+
| [Publish](https://inquilabee.github.io/powernse/maintainer/publish/) | Maintainer release |
|
|
126
|
+
|
|
127
|
+
## Development
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
uv sync
|
|
131
|
+
make check # ShipGate
|
|
132
|
+
make format
|
|
133
|
+
make test
|
|
134
|
+
make build
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Local docs: `uv run --with mkdocs-material mkdocs serve`. Quality gates:
|
|
138
|
+
[ShipGate](https://inquilabee.github.io/shipgate/) (`suite: standard`).
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
MIT — see [LICENSE](LICENSE). Also read [DISCLAIMER.md](DISCLAIMER.md).
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "powernse"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Download and use official NSE India equity archives via CLI and Python"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
keywords = [
|
|
9
|
+
"nse",
|
|
10
|
+
"india",
|
|
11
|
+
"bhavcopy",
|
|
12
|
+
"equities",
|
|
13
|
+
"market-data",
|
|
14
|
+
"download",
|
|
15
|
+
]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Topic :: Office/Business :: Financial :: Investment",
|
|
24
|
+
"Typing :: Typed",
|
|
25
|
+
]
|
|
26
|
+
requires-python = ">=3.13"
|
|
27
|
+
dependencies = [
|
|
28
|
+
"exchange-calendars>=4.5",
|
|
29
|
+
"filelock>=3.32",
|
|
30
|
+
"pydantic-settings>=2.15",
|
|
31
|
+
"requests>=2.32",
|
|
32
|
+
"tenacity>=9.0",
|
|
33
|
+
"typer>=0.27",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[[project.authors]]
|
|
37
|
+
name = "Vishal Mishra"
|
|
38
|
+
email = "vishal.k.mishra2@gmail.com"
|
|
39
|
+
|
|
40
|
+
[project.optional-dependencies]
|
|
41
|
+
pandas = ["pandas>=2.2"]
|
|
42
|
+
|
|
43
|
+
[project.scripts]
|
|
44
|
+
powernse = "powernse.cli:main"
|
|
45
|
+
|
|
46
|
+
[project.urls]
|
|
47
|
+
Homepage = "https://inquilabee.github.io/powernse/"
|
|
48
|
+
Documentation = "https://inquilabee.github.io/powernse/"
|
|
49
|
+
Repository = "https://github.com/inquilabee/powernse"
|
|
50
|
+
Issues = "https://github.com/inquilabee/powernse/issues"
|
|
51
|
+
Changelog = "https://github.com/inquilabee/powernse/blob/main/CHANGELOG.md"
|
|
52
|
+
|
|
53
|
+
[build-system]
|
|
54
|
+
requires = ["uv_build>=0.11.19,<0.12.0"]
|
|
55
|
+
build-backend = "uv_build"
|
|
56
|
+
|
|
57
|
+
[tool.uv]
|
|
58
|
+
default-groups = ["dev"]
|
|
59
|
+
|
|
60
|
+
[tool.ruff]
|
|
61
|
+
line-length = 120
|
|
62
|
+
target-version = "py313"
|
|
63
|
+
|
|
64
|
+
[tool.ruff.lint]
|
|
65
|
+
select = [
|
|
66
|
+
"B",
|
|
67
|
+
"E",
|
|
68
|
+
"F",
|
|
69
|
+
"I",
|
|
70
|
+
"UP",
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
[tool.ruff.lint.per-file-ignores]
|
|
74
|
+
"src/powernse/__init__.py" = ["F401"]
|
|
75
|
+
|
|
76
|
+
[tool.pytest.ini_options]
|
|
77
|
+
testpaths = ["tests"]
|
|
78
|
+
addopts = ["-q"]
|
|
79
|
+
filterwarnings = ["error"]
|
|
80
|
+
|
|
81
|
+
[dependency-groups]
|
|
82
|
+
dev = [
|
|
83
|
+
"pandas>=2.2",
|
|
84
|
+
"pre-commit>=4.6",
|
|
85
|
+
"pytest>=8.3",
|
|
86
|
+
"shipgate>=0.1.11",
|
|
87
|
+
]
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "powernse"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Download and use official NSE India equity archives via CLI and Python"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Vishal Mishra", email = "vishal.k.mishra2@gmail.com" },
|
|
10
|
+
]
|
|
11
|
+
keywords = ["nse", "india", "bhavcopy", "equities", "market-data", "download"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 4 - Beta",
|
|
14
|
+
"Environment :: Console",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.13",
|
|
19
|
+
"Topic :: Office/Business :: Financial :: Investment",
|
|
20
|
+
"Typing :: Typed",
|
|
21
|
+
]
|
|
22
|
+
requires-python = ">=3.13"
|
|
23
|
+
dependencies = [
|
|
24
|
+
"exchange-calendars>=4.5",
|
|
25
|
+
"filelock>=3.32",
|
|
26
|
+
"pydantic-settings>=2.15",
|
|
27
|
+
"requests>=2.32",
|
|
28
|
+
"tenacity>=9.0",
|
|
29
|
+
"typer>=0.27",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
pandas = ["pandas>=2.2"]
|
|
34
|
+
|
|
35
|
+
[project.scripts]
|
|
36
|
+
powernse = "powernse.cli:main"
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://inquilabee.github.io/powernse/"
|
|
40
|
+
Documentation = "https://inquilabee.github.io/powernse/"
|
|
41
|
+
Repository = "https://github.com/inquilabee/powernse"
|
|
42
|
+
Issues = "https://github.com/inquilabee/powernse/issues"
|
|
43
|
+
Changelog = "https://github.com/inquilabee/powernse/blob/main/CHANGELOG.md"
|
|
44
|
+
|
|
45
|
+
[build-system]
|
|
46
|
+
requires = ["uv_build>=0.11.19,<0.12.0"]
|
|
47
|
+
build-backend = "uv_build"
|
|
48
|
+
|
|
49
|
+
[tool.uv]
|
|
50
|
+
default-groups = ["dev"]
|
|
51
|
+
|
|
52
|
+
[dependency-groups]
|
|
53
|
+
dev = [
|
|
54
|
+
"pandas>=2.2",
|
|
55
|
+
"pre-commit>=4.6",
|
|
56
|
+
"pytest>=8.3",
|
|
57
|
+
"shipgate>=0.1.11",
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
[tool.ruff]
|
|
61
|
+
line-length = 120
|
|
62
|
+
target-version = "py313"
|
|
63
|
+
|
|
64
|
+
[tool.ruff.lint]
|
|
65
|
+
select = ["B", "E", "F", "I", "UP"]
|
|
66
|
+
|
|
67
|
+
[tool.pytest.ini_options]
|
|
68
|
+
testpaths = ["tests"]
|
|
69
|
+
addopts = ["-q"]
|
|
70
|
+
filterwarnings = ["error"]
|
|
71
|
+
|
|
72
|
+
[tool.ruff.lint.per-file-ignores]
|
|
73
|
+
"src/powernse/__init__.py" = ["F401"]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""PowerNSE — download and use official NSE India equity archives."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
+
|
|
5
|
+
from powernse.archive import ArchiveRoot
|
|
6
|
+
from powernse.calendar import iter_trading_dates
|
|
7
|
+
from powernse.data import NSEData
|
|
8
|
+
from powernse.downloaders import (
|
|
9
|
+
BhavcopyDownloader,
|
|
10
|
+
BlockDealsDownloader,
|
|
11
|
+
BulkDealsDownloader,
|
|
12
|
+
CorporateActionsDownloader,
|
|
13
|
+
FoBhavcopyDownloader,
|
|
14
|
+
FoSecbanDownloader,
|
|
15
|
+
FullBhavcopyDownloader,
|
|
16
|
+
IndexClosesDownloader,
|
|
17
|
+
IndexConstituentsDownloader,
|
|
18
|
+
bhavcopy_archive_url,
|
|
19
|
+
)
|
|
20
|
+
from powernse.errors import ArchiveError, DownloadError, PayloadError, PowerNseError
|
|
21
|
+
from powernse.loaders import ArchiveReader
|
|
22
|
+
from powernse.settings import Settings
|
|
23
|
+
from powernse.types import AdjustedOhlcBar, DownloadSummary, FoBar, IndexBar, OhlcBar
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"ArchiveError",
|
|
27
|
+
"ArchiveReader",
|
|
28
|
+
"ArchiveRoot",
|
|
29
|
+
"AdjustedOhlcBar",
|
|
30
|
+
"BhavcopyDownloader",
|
|
31
|
+
"BlockDealsDownloader",
|
|
32
|
+
"BulkDealsDownloader",
|
|
33
|
+
"CorporateActionsDownloader",
|
|
34
|
+
"DownloadError",
|
|
35
|
+
"DownloadSummary",
|
|
36
|
+
"FoBar",
|
|
37
|
+
"FoBhavcopyDownloader",
|
|
38
|
+
"FoSecbanDownloader",
|
|
39
|
+
"FullBhavcopyDownloader",
|
|
40
|
+
"IndexBar",
|
|
41
|
+
"IndexClosesDownloader",
|
|
42
|
+
"IndexConstituentsDownloader",
|
|
43
|
+
"NSEData",
|
|
44
|
+
"OhlcBar",
|
|
45
|
+
"PayloadError",
|
|
46
|
+
"PowerNseError",
|
|
47
|
+
"Settings",
|
|
48
|
+
"bhavcopy_archive_url",
|
|
49
|
+
"iter_trading_dates",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def package_version() -> str:
|
|
54
|
+
try:
|
|
55
|
+
return version("powernse")
|
|
56
|
+
except PackageNotFoundError:
|
|
57
|
+
return "0.0.0+local"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
__version__ = package_version()
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Opt-in corporate-action adjustment helpers for equity OHLC."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from datetime import date
|
|
5
|
+
|
|
6
|
+
from powernse.downloaders.corporate_actions import parse_corporate_action_date
|
|
7
|
+
from powernse.types import AdjustedOhlcBar, OhlcBar
|
|
8
|
+
|
|
9
|
+
BONUS_RATIO = re.compile(r"bonus\s*(?:issue\s*)?(\d+)\s*:\s*(\d+)", re.IGNORECASE)
|
|
10
|
+
SPLIT_RATIO = re.compile(
|
|
11
|
+
r"(?:split|face\s*value\s*split).*?(\d+)\s*(?:for|:|/)\s*(\d+)",
|
|
12
|
+
re.IGNORECASE,
|
|
13
|
+
)
|
|
14
|
+
SPLIT_TO_FROM = re.compile(
|
|
15
|
+
r"face\s*value\s*split.*?from\s*(?:rs\.?\s*)?(\d+(?:\.\d+)?).*?to\s*(?:rs\.?\s*)?(\d+(?:\.\d+)?)",
|
|
16
|
+
re.IGNORECASE,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def price_adjustment_factor_from_subject(subject: str) -> float | None:
|
|
21
|
+
"""Return a price divisor for a CA subject, or None when unrecognized.
|
|
22
|
+
|
|
23
|
+
Bonus ``A:B`` (A shares for B held) → factor ``(A+B)/B``.
|
|
24
|
+
Face-value from→to → ``old/new``. Split ``N for M`` → larger/smaller share multiple.
|
|
25
|
+
"""
|
|
26
|
+
text = subject.strip()
|
|
27
|
+
if not text:
|
|
28
|
+
return None
|
|
29
|
+
bonus = BONUS_RATIO.search(text)
|
|
30
|
+
if bonus is not None:
|
|
31
|
+
bonus_shares = int(bonus.group(1))
|
|
32
|
+
held = int(bonus.group(2))
|
|
33
|
+
if held <= 0:
|
|
34
|
+
return None
|
|
35
|
+
return (bonus_shares + held) / held
|
|
36
|
+
face = SPLIT_TO_FROM.search(text)
|
|
37
|
+
if face is not None:
|
|
38
|
+
old_fv = float(face.group(1))
|
|
39
|
+
new_fv = float(face.group(2))
|
|
40
|
+
if new_fv <= 0:
|
|
41
|
+
return None
|
|
42
|
+
return old_fv / new_fv
|
|
43
|
+
split = SPLIT_RATIO.search(text)
|
|
44
|
+
if split is not None:
|
|
45
|
+
left = int(split.group(1))
|
|
46
|
+
right = int(split.group(2))
|
|
47
|
+
if left <= 0 or right <= 0:
|
|
48
|
+
return None
|
|
49
|
+
return max(left, right) / min(left, right)
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def corporate_action_price_events(records: list[dict[str, object]]) -> list[tuple[date, float]]:
|
|
54
|
+
"""Extract ``(ex_date, price_divisor)`` pairs from corporate-action records."""
|
|
55
|
+
events: list[tuple[date, float]] = []
|
|
56
|
+
for record in records:
|
|
57
|
+
subject = str(record.get("subject") or record.get("SUBJECT") or "")
|
|
58
|
+
factor = price_adjustment_factor_from_subject(subject)
|
|
59
|
+
if factor is None or factor == 1.0:
|
|
60
|
+
continue
|
|
61
|
+
ex_date = parse_corporate_action_date(record)
|
|
62
|
+
if ex_date is None:
|
|
63
|
+
continue
|
|
64
|
+
events.append((ex_date, factor))
|
|
65
|
+
events.sort(key=lambda item: item[0])
|
|
66
|
+
return events
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def apply_price_adjustments(bars: list[OhlcBar], events: list[tuple[date, float]]) -> list[AdjustedOhlcBar]:
|
|
70
|
+
"""Apply cumulative CA factors so the newest bar keeps factor 1.0."""
|
|
71
|
+
if not bars:
|
|
72
|
+
return []
|
|
73
|
+
ordered = sorted(bars, key=lambda bar: bar.trade_date)
|
|
74
|
+
cumulative = 1.0
|
|
75
|
+
event_index = len(events) - 1
|
|
76
|
+
result_rev: list[AdjustedOhlcBar] = []
|
|
77
|
+
for i, bar in enumerate(reversed(ordered)):
|
|
78
|
+
newer = ordered[len(ordered) - i] if i > 0 else None
|
|
79
|
+
upper = newer.trade_date if newer is not None else date.max
|
|
80
|
+
while event_index >= 0 and bar.trade_date < events[event_index][0] <= upper:
|
|
81
|
+
cumulative *= events[event_index][1]
|
|
82
|
+
event_index -= 1
|
|
83
|
+
result_rev.append(
|
|
84
|
+
AdjustedOhlcBar(
|
|
85
|
+
trade_date=bar.trade_date,
|
|
86
|
+
symbol=bar.symbol,
|
|
87
|
+
series=bar.series,
|
|
88
|
+
open=bar.open / cumulative,
|
|
89
|
+
high=bar.high / cumulative,
|
|
90
|
+
low=bar.low / cumulative,
|
|
91
|
+
close=bar.close / cumulative,
|
|
92
|
+
volume=int(round(bar.volume * cumulative)),
|
|
93
|
+
factor=cumulative,
|
|
94
|
+
isin=bar.isin,
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
while event_index >= 0 and events[event_index][0] == bar.trade_date:
|
|
98
|
+
cumulative *= events[event_index][1]
|
|
99
|
+
event_index -= 1
|
|
100
|
+
return list(reversed(result_rev))
|