Resetting utils subtree.

This commit is contained in:
2024-12-12 11:47:19 +05:30
parent 9a3ef5fea4
commit 0602870bf0
132 changed files with 157 additions and 101313 deletions
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-281
View File
@@ -1,281 +0,0 @@
"""
AUTHOR:
Khushal P Soonderji
DATE:
Thursday, 28th Nov., 2024
OBJECTIVE:
To provide a way to retrieve dates of important events like financial-results, stock-splits, fund-raising, etc.
from NSE's portal.
NOTE: This method involves web scraping. It is good for proof-of-concept development, but it is recommended that
more professional data-sources be used when the product starts becoming mature.
REFERENCES:
N/A
DOWNLOADS:
N/A
"""
# *****************************************************************************************************************
# ***** ****
# *** IMPORT ***
# ***** ****
# *****************************************************************************************************************
# To make sibling directories accessible for imports:
import sys
sys.path.append(".")
sys.path.append("..")
# System-level activities:
import io
# My utils:
from utils_v2.string import json
from utils_v2.date_time import date_time
# NSE-related utils:
from utils_v2.nse.base import AsyncNSEBase
from utils_v2.nse.models.data.api_call import NSEApiResponse
# To make REST-ful API calls:
import httpx
# To work with date and time:
import datetime
# To work with datatypes:
from typing import Any, List
# For asynchronous activities:
import asyncio
# *****************************************************************************************************************
# ***** ****
# *** MACROS / ONE-TIME INIT ***
# ***** ****
# *****************************************************************************************************************
# --- Nothing Yet
# *****************************************************************************************************************
# ***** ****
# *** VARIABLES ***
# ***** ****
# *****************************************************************************************************************
# --- Nothing Yet
# *****************************************************************************************************************
# ***** ****
# *** FUNCTIONS ***
# ***** ****
# *****************************************************************************************************************
class NSEIndexOptionChain(AsyncNSEBase):
# Index Names:
INDEX_NIFTY = "NIFTY"
INDEX_BANKNIFTY = "BANKNIFTY"
INDEX_FINNIFTY = "FINNIFTY"
INDEX_MIDCPNIFTY = "MIDCPNIFTY"
INDEX_NIFTYNXT50 = "NIFTYNXT50"
def __init__(
self,
http_client: httpx.AsyncClient,
debug = True,
debug_prefix = "NSE (IdxOC) | ",
debug_only_errors = True
):
# Pass on the initialization to the parent:
super().__init__(
base_url = r"https://www.nseindia.com/option-chain",
data_url = r"https://www.nseindia.com/api/option-chain-indices",
http_client = http_client,
debug = debug,
debug_prefix = debug_prefix,
debug_only_errors = debug_only_errors
)
async def get_data(
self,
index_name: str,
return_raw: bool = False
) -> NSEApiResponse:
"""
To get the data of the corporate event calendar.
:param index_name: The name of the index whose option chain is needed. Use one of the options created within the
scope of this class.
:param return_raw: Whether you want the raw JSON from NSE or you want it formatted.
:return: The raw or formatted event calendar data in the 'data' field of the response model.
"""
# Make the API call:
api_response = await self.get(params = {"symbol": index_name})
# If the API call was successful:
if api_response.httpCode in [200]:
api_response.success = True
if return_raw: api_response.data = await api_response.get_json()
else:
try: api_response.data = self.format_data(
raw_json = await api_response.get_json(),
timestamp = date_time.get_current_utc_date_time(as_string = True),
raise_exception = True
)
except Exception as exception:
api_response.exception = exception
api_response.success = False
# Done here:
return api_response
@staticmethod
def format_data(
raw_json: dict,
timestamp: datetime.datetime = None,
raise_exception: bool = False
) -> List[dict] | None:
"""
We format the data here to be able to retrieve it properly later.
:param raw_json: The raw data as scraped from NSE.
:param timestamp: The timestamp at which the data was scraped. This shall be useful for data retrieval from the
database, later.
:param raise_exception: If set to True, any exception will be propagated. If set to False, any exception will be
suppressed internally.
:return: The formatted data if successful, else None.
"""
# Can't do anything if the chain itself is null:
if raw_json is None: return raw_json
# Start by assuming failure:
formatted_chain = None
# Ensure that we've got a proper timestamp:
if timestamp is None: timestamp = date_time.get_current_utc_date_time(as_string = False)
try:
# Start at the expiry-level:
expiry_dates = raw_json["records"]["expiryDates"]
semi_formatted_chain = {e: None for e in expiry_dates}
# Iterate through the raw records and start plugging them into the formatted records:
for record in raw_json["records"]["data"]:
# Extract some variables:
expiry_date = record["expiryDate"]
strike_price = record["strikePrice"]
# Start building the needed structure:
if semi_formatted_chain.get(expiry_date) is None: semi_formatted_chain[expiry_date] = {}
if semi_formatted_chain[expiry_date].get(strike_price) is None:
semi_formatted_chain[expiry_date][strike_price] = {
"ts": timestamp,
"strike": record["strikePrice"],
"expiry": record["expiryDate"]
}
# Add the CE/PE data:
for right in ["CE", "PE"]:
right_data = record.get(right, {})
if right_data:
semi_formatted_chain[expiry_date][strike_price]["underlying"] = right_data["underlying"]
semi_formatted_chain[expiry_date][strike_price]["underlyingValue"] = right_data["underlyingValue"]
semi_formatted_chain[expiry_date][strike_price][right.lower()] = {}
semi_formatted_chain[expiry_date][strike_price][right.lower()] = {
"id": right_data.get("identifier"),
"oi": right_data.get("openInterest"),
"oiChg": right_data.get("changeinOpenInterest"),
"oiPctChg": right_data.get("pchangeinOpenInterest"),
"totTradedVol": right_data.get("totalTradedVolume"),
"iv": right_data.get("impliedVolatility"),
"ltp": right_data.get("lastPrice"),
"change": right_data.get("change"),
"pChange": right_data.get("pChange"),
"totBuyQty": right_data.get("totalBuyQuantity"),
"totSellQty": right_data.get("totalSellQuantity"),
"bidQty": right_data.get("bidQty"),
"bidPrice": right_data.get("bidprice"),
"askQty": right_data.get("askQty"),
"askPrice": right_data.get("askPrice")
}
# Final formatting:
formatted_chain = []
for expiry, _0 in semi_formatted_chain.items():
if isinstance(_0, dict):
for strike_price, _1 in _0.items():
_1["ce"] = _1.pop("ce")
_1["pe"] = _1.pop("pe")
formatted_chain.append(_1)
# If something goes wrong:
except Exception as exception:
formatted_chain = None
if raise_exception: raise
# Done here:
return formatted_chain
# *****************************************************************************************************************
# ***** ****
# *** MAIN PROGRAM ***
# ***** ****
# *****************************************************************************************************************
if __name__ == "__main__":
async def main():
# Create an HTTP client:
test_client = httpx.AsyncClient(
limits = httpx.Limits(
max_connections = 100, # ............ Maximum number of connections allowed in the pool.
max_keepalive_connections = 50, # ... Maximum number of connections that can be kept alive.
),
timeout = httpx.Timeout(
connect = 2.5, # ... Shorter connection timeout.
read = 2.5, # ...... Like what EasyEcom gives.
write = 10.0, # .... Time to wait for sending data.
pool = 120.0 # ..... Time to wait for a free connection from the pool.
)
)
# Create an instance of the scraper, and refresh its cookies:
my_nse = NSEIndexOptionChain(http_client = test_client)
# Get and show the data:
api_response = await my_nse.get_data(
index_name = NSEIndexOptionChain.INDEX_NIFTY,
return_raw = False
)
print("SUMMARY:", api_response.to_markdown(), "\n---\n\n")
if api_response.success: print("OPTION CHAIN:", json.to_string(api_response.data, default = str))
asyncio.run(main())