nsepython 2.99__py3-none-any.whl → 2.101__py3-none-any.whl
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.
- nsepython/__init__.py +8 -1
- nsepython/rahu.py +157 -17
- {nsepython-2.99.dist-info → nsepython-2.101.dist-info}/METADATA +1 -1
- nsepython-2.101.dist-info/RECORD +7 -0
- nsepython-2.99.dist-info/RECORD +0 -7
- {nsepython-2.99.dist-info → nsepython-2.101.dist-info}/WHEEL +0 -0
- {nsepython-2.99.dist-info → nsepython-2.101.dist-info}/licenses/LICENSE +0 -0
- {nsepython-2.99.dist-info → nsepython-2.101.dist-info}/top_level.txt +0 -0
nsepython/__init__.py
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
from .rahu import *
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
try:
|
|
4
|
+
from importlib.metadata import version as _pkg_version
|
|
5
|
+
__version__ = _pkg_version("nsepython")
|
|
6
|
+
except Exception:
|
|
7
|
+
# Not pip-installed (e.g. running straight from a git checkout with no
|
|
8
|
+
# installed dist) -- fall back to the last known release rather than
|
|
9
|
+
# raising PackageNotFoundError and breaking the import for everyone.
|
|
10
|
+
__version__ = "2.101"
|
nsepython/rahu.py
CHANGED
|
@@ -597,24 +597,56 @@ def nse_quote(symbol,section=""):
|
|
|
597
597
|
f"run/populated yet."
|
|
598
598
|
)
|
|
599
599
|
|
|
600
|
-
#
|
|
601
|
-
#
|
|
600
|
+
# v2.101 fix: the Round-3 rationale above ("NSE's old API never accepted
|
|
601
|
+
# these section values") is true but beside the point -- this isn't
|
|
602
|
+
# about replicating the old query parameter, it's about reconstructing
|
|
603
|
+
# the equivalent DATA from the new no-section payload, exactly as the
|
|
604
|
+
# 'trade_info' branch above already does. nsepythonserver already does
|
|
605
|
+
# this for the remaining old quote-equity top-level keys (info/metadata/
|
|
606
|
+
# priceInfo/securityInfo/industryInfo); port that here too. Every one of
|
|
607
|
+
# these is a pure re-slice of the same GetQuoteApi?functionName=
|
|
608
|
+
# getSymbolData payload nse_quote(symbol, section="") already fetches --
|
|
609
|
+
# no new network call. 'preOpenMarket' is deliberately NOT included in
|
|
610
|
+
# this set: the branch above already serves it from a genuinely live,
|
|
611
|
+
# higher-fidelity endpoint (/api/market-data-pre-open) rather than this
|
|
612
|
+
# reshape's coarser approximation.
|
|
613
|
+
_equity_detail_sections = {
|
|
614
|
+
"info", "metadata", "priceInfo", "securityInfo", "industryInfo",
|
|
615
|
+
}
|
|
616
|
+
if section in _equity_detail_sections:
|
|
617
|
+
base_payload = nse_quote(symbol, section="")
|
|
618
|
+
if 'equityResponse' not in base_payload or not base_payload['equityResponse']:
|
|
619
|
+
# Index/derivative symbols have no cash-market EquityDetails
|
|
620
|
+
# shape to slice a section out of.
|
|
621
|
+
raise NSEEndpointError(
|
|
622
|
+
f"nse_quote({symbol!r}, section={section!r}): no equity "
|
|
623
|
+
f"'section' data exists for index/derivative symbols -- use "
|
|
624
|
+
f"nse_quote_derivatives(symbol) or nse_quote(symbol) (no "
|
|
625
|
+
f"section) instead."
|
|
626
|
+
)
|
|
627
|
+
if section == "industryInfo":
|
|
628
|
+
sec_info = base_payload['equityResponse'][0].get('secInfo', {}) or {}
|
|
629
|
+
return {
|
|
630
|
+
"macro": sec_info.get("macro"),
|
|
631
|
+
"sector": sec_info.get("sector"),
|
|
632
|
+
"industry": sec_info.get("industryInfo"),
|
|
633
|
+
"basicIndustry": sec_info.get("basicIndustry"),
|
|
634
|
+
}
|
|
635
|
+
reshaped = _reshape_equity_quote(base_payload)
|
|
636
|
+
return reshaped.get(section, {})
|
|
637
|
+
|
|
638
|
+
# Round 3: every other section value used to fall through
|
|
602
639
|
# here and hit the dead /api/quote-equity§ion=X route -- a ~5s
|
|
603
640
|
# double-retry ending in a misleading HTTP 403, for a route that was
|
|
604
|
-
# never real in the first place.
|
|
605
|
-
#
|
|
606
|
-
#
|
|
607
|
-
# any section value beyond 'trade_info' -- those other names are just
|
|
608
|
-
# top-level keys inside the un-sectioned response, already returned in
|
|
609
|
-
# full by nse_quote(symbol) (section=""). Raise immediately and clearly
|
|
610
|
-
# instead of a slow, confusing network round-trip to a route that was
|
|
611
|
-
# never real.
|
|
641
|
+
# never real in the first place. Raise immediately and clearly instead
|
|
642
|
+
# of a slow, confusing network round-trip to a route that was never
|
|
643
|
+
# real.
|
|
612
644
|
raise ValueError(
|
|
613
|
-
f"nse_quote: unsupported section={section!r};
|
|
614
|
-
f"'trade_info',
|
|
615
|
-
f"
|
|
616
|
-
f"
|
|
617
|
-
f"
|
|
645
|
+
f"nse_quote: unsupported section={section!r}; supported: '' (full "
|
|
646
|
+
f"quote), 'trade_info', 'preOpenMarket', and "
|
|
647
|
+
f"{sorted(_equity_detail_sections)}. section='' already returns the "
|
|
648
|
+
f"full detail (metaData/secInfo/priceInfo/orderBook/tradeInfo all "
|
|
649
|
+
f"together)."
|
|
618
650
|
)
|
|
619
651
|
def nse_expirydetails(payload, i=0, symbol=None):
|
|
620
652
|
expiry_dates = []
|
|
@@ -1091,6 +1123,63 @@ def nse_optionchain_ltp(payload,strikePrice,optionType,inp=0,intent=""):
|
|
|
1091
1123
|
"directly."
|
|
1092
1124
|
)
|
|
1093
1125
|
|
|
1126
|
+
def _reshape_equity_quote(raw):
|
|
1127
|
+
"""/api/quote-equity (nse_eq's old data source) is dead (confirmed live
|
|
1128
|
+
403 Access Denied, Apache/WAF-style, not an Akamai JS challenge -- there
|
|
1129
|
+
is no cookie that fixes it). The live quote page itself now calls
|
|
1130
|
+
GetQuoteApi?functionName=getSymbolData instead, which carries nearly all
|
|
1131
|
+
the same information, just regrouped. We reshape it back into the old
|
|
1132
|
+
quote-equity top-level key names (info/metadata/priceInfo/securityInfo/
|
|
1133
|
+
tradeInfo) as closely as the new payload allows, so code written against
|
|
1134
|
+
the old shape (payload['priceInfo']['lastPrice'],
|
|
1135
|
+
payload['metadata']['pdSymbolPe'], etc.) keeps working. The full
|
|
1136
|
+
unmodified NextApi response is also kept under '_raw' for anyone who
|
|
1137
|
+
wants the new field names directly. Ported from nsepythonserver's
|
|
1138
|
+
identical helper (same live-verified field mapping)."""
|
|
1139
|
+
if not raw or 'equityResponse' not in raw or not raw['equityResponse']:
|
|
1140
|
+
return raw
|
|
1141
|
+
row = raw['equityResponse'][0]
|
|
1142
|
+
meta = row.get('metaData', {}) or {}
|
|
1143
|
+
sec = row.get('secInfo', {}) or {}
|
|
1144
|
+
trade = row.get('tradeInfo', {}) or {}
|
|
1145
|
+
price = row.get('priceInfo', {}) or {}
|
|
1146
|
+
order = row.get('orderBook', {}) or {}
|
|
1147
|
+
|
|
1148
|
+
reshaped = {
|
|
1149
|
+
'info': {
|
|
1150
|
+
'symbol': meta.get('symbol'),
|
|
1151
|
+
'companyName': meta.get('companyName'),
|
|
1152
|
+
'industry': sec.get('basicIndustry'),
|
|
1153
|
+
'isin': meta.get('isinCode'),
|
|
1154
|
+
'series': meta.get('series'),
|
|
1155
|
+
},
|
|
1156
|
+
'metadata': {
|
|
1157
|
+
**meta,
|
|
1158
|
+
'pdSectorPe': sec.get('pdSectorPe'),
|
|
1159
|
+
'pdSymbolPe': sec.get('pdSymbolPe'),
|
|
1160
|
+
'pdSectorInd': sec.get('pdSectorInd'),
|
|
1161
|
+
},
|
|
1162
|
+
'priceInfo': {
|
|
1163
|
+
'lastPrice': order.get('lastPrice', meta.get('closePrice')),
|
|
1164
|
+
'change': meta.get('change'),
|
|
1165
|
+
'pChange': meta.get('pChange'),
|
|
1166
|
+
'previousClose': meta.get('previousClose'),
|
|
1167
|
+
'open': meta.get('open'),
|
|
1168
|
+
'close': meta.get('closePrice'),
|
|
1169
|
+
'vwap': meta.get('averagePrice'),
|
|
1170
|
+
'intraDayHighLow': {'min': meta.get('dayLow'), 'max': meta.get('dayHigh'),
|
|
1171
|
+
'value': order.get('lastPrice', meta.get('closePrice'))},
|
|
1172
|
+
'weekHighLow': {'min': price.get('yearLow'), 'max': price.get('yearHigh'),
|
|
1173
|
+
'minDate': price.get('yearLowDt'), 'maxDate': price.get('yearHightDt')},
|
|
1174
|
+
},
|
|
1175
|
+
'securityInfo': sec,
|
|
1176
|
+
'tradeInfo': trade,
|
|
1177
|
+
'preOpenMarket': {},
|
|
1178
|
+
'_raw': raw,
|
|
1179
|
+
}
|
|
1180
|
+
return reshaped
|
|
1181
|
+
|
|
1182
|
+
|
|
1094
1183
|
def nse_eq(symbol):
|
|
1095
1184
|
symbol = nsesymbolpurify(symbol)
|
|
1096
1185
|
try:
|
|
@@ -1108,13 +1197,21 @@ def nse_eq(symbol):
|
|
|
1108
1197
|
# same underlying data (just in a different JSON shape - data lives
|
|
1109
1198
|
# under payload['equityResponse'][0] instead of payload['priceInfo']/
|
|
1110
1199
|
# payload['info']) so we fall back to that instead of returning {}.
|
|
1200
|
+
#
|
|
1201
|
+
# v2.101 fix: the fallback used to return that raw NextApi payload
|
|
1202
|
+
# unreshaped, so old bracket-access code (payload['priceInfo']
|
|
1203
|
+
# ['lastPrice'], payload['metadata']['pdSectorPe'], etc. -- the exact
|
|
1204
|
+
# examples long documented on unofficed.com's Basic Functions page)
|
|
1205
|
+
# raised KeyError instead of working, a real regression introduced
|
|
1206
|
+
# by this same Akamai fallback. nsepythonserver already carried this
|
|
1207
|
+
# reshape; port it here too via _reshape_equity_quote() above.
|
|
1111
1208
|
logging.warning(
|
|
1112
1209
|
"nse_eq(%s): /api/quote-equity is retired; returning data from "
|
|
1113
1210
|
"the newer NextApi quote endpoint instead (see nse_quote() - the "
|
|
1114
1211
|
"JSON shape differs from the old quote-equity response).",
|
|
1115
1212
|
symbol,
|
|
1116
1213
|
)
|
|
1117
|
-
payload = nse_quote(symbol)
|
|
1214
|
+
payload = _reshape_equity_quote(nse_quote(symbol))
|
|
1118
1215
|
return payload
|
|
1119
1216
|
|
|
1120
1217
|
|
|
@@ -1140,6 +1237,16 @@ def nse_fno(symbol):
|
|
|
1140
1237
|
symbol,
|
|
1141
1238
|
)
|
|
1142
1239
|
payload = nse_quote_derivatives(symbol)
|
|
1240
|
+
# v2.101 fix: the old quote-derivative response carried 'underlyingValue'
|
|
1241
|
+
# at the top level; the new flat getSymbolDerivativesData shape instead
|
|
1242
|
+
# repeats it inside EVERY entry of payload['data'] (same value on each),
|
|
1243
|
+
# so payload['underlyingValue'] (a documented, long-standing access
|
|
1244
|
+
# pattern) now raises KeyError. Promote it back to the top level from
|
|
1245
|
+
# the first entry without touching/removing the existing 'data' key.
|
|
1246
|
+
if 'underlyingValue' not in payload and payload.get('data'):
|
|
1247
|
+
first = payload['data'][0]
|
|
1248
|
+
if isinstance(first, dict) and 'underlyingValue' in first:
|
|
1249
|
+
payload['underlyingValue'] = first['underlyingValue']
|
|
1143
1250
|
return payload
|
|
1144
1251
|
|
|
1145
1252
|
def quote_equity(symbol):
|
|
@@ -1773,6 +1880,39 @@ def share_holding(symbol):
|
|
|
1773
1880
|
return df
|
|
1774
1881
|
|
|
1775
1882
|
|
|
1883
|
+
def nse_annual_reports(symbol, year_from=None, year_to=None, index="equities"):
|
|
1884
|
+
"""Forum feature request (forum.unofficed.com topic 1459): list a listed
|
|
1885
|
+
company's annual reports with direct PDF download links, optionally
|
|
1886
|
+
filtered to a from/to year range. Backed by
|
|
1887
|
+
`/api/annual-reports?index=equities&symbol=X`, confirmed live via
|
|
1888
|
+
curl_cffi -- NOT the Akamai-walled historical/* family, just a plain
|
|
1889
|
+
metadata+link listing, so this is reliable. `index` is almost always
|
|
1890
|
+
"equities" (NSE also recognises "debt", but that returns no rows for a
|
|
1891
|
+
pure-equity symbol like most NSE-listed companies). Each returned row's
|
|
1892
|
+
`fileName` column IS the direct downloadable PDF/zip URL on
|
|
1893
|
+
nsearchives.nseindia.com -- fetch it yourself (e.g. with `requests` or
|
|
1894
|
+
`curl`) if you want the actual file; this function returns the metadata
|
|
1895
|
+
+ link, matching how every other *_history-style function in this
|
|
1896
|
+
library works, rather than silently downloading files to disk.
|
|
1897
|
+
`year_from`/`year_to` filter client-side on the report's `fromYr` (NSE's
|
|
1898
|
+
own API has no year-range parameter, it always returns the full
|
|
1899
|
+
available history -- commonly 15-20+ years for large-cap companies)."""
|
|
1900
|
+
symbol = nsesymbolpurify(symbol)
|
|
1901
|
+
payload = nsefetch(f"https://www.nseindia.com/api/annual-reports?index={index}&symbol={symbol}")
|
|
1902
|
+
rows = payload.get("data") or []
|
|
1903
|
+
df = pd.DataFrame.from_records(rows)
|
|
1904
|
+
if df.empty:
|
|
1905
|
+
return df
|
|
1906
|
+
if year_from is not None or year_to is not None:
|
|
1907
|
+
years = pd.to_numeric(df["fromYr"], errors="coerce")
|
|
1908
|
+
if year_from is not None:
|
|
1909
|
+
df = df[years >= int(year_from)]
|
|
1910
|
+
years = years[df.index]
|
|
1911
|
+
if year_to is not None:
|
|
1912
|
+
df = df[years <= int(year_to)]
|
|
1913
|
+
return df.reset_index(drop=True)
|
|
1914
|
+
|
|
1915
|
+
|
|
1776
1916
|
#Request from subhash
|
|
1777
1917
|
## https://unofficed.com/how-to-find-the-beta-of-indian-stocks-using-python/
|
|
1778
1918
|
def get_beta_df_maker(symbol,days):
|
|
@@ -2101,7 +2241,7 @@ _NSE_MCP_SERVERS = {
|
|
|
2101
2241
|
"cmmkt": "https://mcp.nseindia.in/cmmkt/mcp",
|
|
2102
2242
|
}
|
|
2103
2243
|
|
|
2104
|
-
_NSE_MCP_CLIENT_VERSION = "2.
|
|
2244
|
+
_NSE_MCP_CLIENT_VERSION = "2.101"
|
|
2105
2245
|
|
|
2106
2246
|
# Lightweight session-id cache, keyed by server URL, so repeated calls to the
|
|
2107
2247
|
# same MCP server don't re-run the "initialize" handshake every time.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
nsepython/__init__.py,sha256=-f60TXCASPEAVtjuWEWaRIiHM5RnB2MkECBbUgHxJKU,394
|
|
2
|
+
nsepython/rahu.py,sha256=UKqMUe8PyHCVwM1vjnfzJr46_lXPCnL2Kqgmt9-J9FM,131441
|
|
3
|
+
nsepython-2.101.dist-info/licenses/LICENSE,sha256=VXapH-nuSz68DG5ScURElxHkl1lKk4MOK9cgZCSRDXs,34502
|
|
4
|
+
nsepython-2.101.dist-info/METADATA,sha256=REcoHhc7sqwr8IOJ3QWOxcUKP1cXbopNvxqzHUqsIdg,7534
|
|
5
|
+
nsepython-2.101.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
6
|
+
nsepython-2.101.dist-info/top_level.txt,sha256=-hTS08c-pS4rPpiuzLkamRsgHTq7t6BG0M5wnU-iPy4,10
|
|
7
|
+
nsepython-2.101.dist-info/RECORD,,
|
nsepython-2.99.dist-info/RECORD
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
nsepython/__init__.py,sha256=_1YN_O25fj7GRUrOREinuxKiXiF0l4cs-Grzjec8mZg,41
|
|
2
|
-
nsepython/rahu.py,sha256=FTz7FfZOb2MilRtt1ENAxoHvFZtUC6J0A84mKQMuzis,124167
|
|
3
|
-
nsepython-2.99.dist-info/licenses/LICENSE,sha256=VXapH-nuSz68DG5ScURElxHkl1lKk4MOK9cgZCSRDXs,34502
|
|
4
|
-
nsepython-2.99.dist-info/METADATA,sha256=Ib_NPqQNPLNxP79vidhr1brGaj3SZbcXRL5mYUm3YZM,7533
|
|
5
|
-
nsepython-2.99.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
6
|
-
nsepython-2.99.dist-info/top_level.txt,sha256=-hTS08c-pS4rPpiuzLkamRsgHTq7t6BG0M5wnU-iPy4,10
|
|
7
|
-
nsepython-2.99.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|