''' | |
* Project : Screenipy | |
* Author : Pranjal Joshi | |
* Created : 28/04/2021 | |
* Description : Class for handling networking for fetching stock codes and data | |
''' | |
import sys | |
import urllib.request | |
import csv | |
import requests | |
import random | |
import os | |
import datetime | |
import yfinance as yf | |
import pandas as pd | |
from nsetools import Nse | |
from classes.ColorText import colorText | |
from classes.SuppressOutput import SuppressOutput | |
from classes.Utility import isDocker | |
nse = Nse() | |
# Exception class if yfinance stock delisted | |
class StockDataEmptyException(Exception): | |
pass | |
# This Class Handles Fetching of Stock Data over the internet | |
class tools: | |
def __init__(self, configManager): | |
self.configManager = configManager | |
pass | |
def getAllNiftyIndices(self) -> dict: | |
return { | |
"^NSEI": "NIFTY 50", | |
"^NSMIDCP": "NIFTY NEXT 50", | |
"^CNX100": "NIFTY 100", | |
"^CNX200": "NIFTY 200", | |
"^CNX500": "NIFTY 500", | |
"^NSEMDCP50": "NIFTY MIDCAP 50", | |
"NIFTY_MIDCAP_100.NS": "NIFTY MIDCAP 100", | |
"^CNXSC": "NIFTY SMALLCAP 100", | |
"^INDIAVIX": "INDIA VIX", | |
"NIFTYMIDCAP150.NS": "NIFTY MIDCAP 150", | |
"NIFTYSMLCAP50.NS": "NIFTY SMALLCAP 50", | |
"NIFTYSMLCAP250.NS": "NIFTY SMALLCAP 250", | |
"NIFTYMIDSML400.NS": "NIFTY MIDSMALLCAP 400", | |
"NIFTY500_MULTICAP.NS": "NIFTY500 MULTICAP 50:25:25", | |
"NIFTY_LARGEMID250.NS": "NIFTY LARGEMIDCAP 250", | |
"NIFTY_MID_SELECT.NS": "NIFTY MIDCAP SELECT", | |
"NIFTY_TOTAL_MKT.NS": "NIFTY TOTAL MARKET", | |
"NIFTY_MICROCAP250.NS": "NIFTY MICROCAP 250", | |
"^NSEBANK": "NIFTY BANK", | |
"^CNXAUTO": "NIFTY AUTO", | |
"NIFTY_FIN_SERVICE.NS": "NIFTY FINANCIAL SERVICES", | |
"^CNXFMCG": "NIFTY FMCG", | |
"^CNXIT": "NIFTY IT", | |
"^CNXMEDIA": "NIFTY MEDIA", | |
"^CNXMETAL": "NIFTY METAL", | |
"^CNXPHARMA": "NIFTY PHARMA", | |
"^CNXPSUBANK": "NIFTY PSU BANK", | |
"^CNXREALTY": "NIFTY REALTY", | |
"NIFTY_HEALTHCARE.NS": "NIFTY HEALTHCARE INDEX", | |
"NIFTY_CONSR_DURBL.NS": "NIFTY CONSUMER DURABLES", | |
"NIFTY_OIL_AND_GAS.NS": "NIFTY OIL & GAS", | |
"NIFTYALPHA50.NS": "NIFTY ALPHA 50", | |
"^CNXCMDT": "NIFTY COMMODITIES", | |
"NIFTY_CPSE.NS": "NIFTY CPSE", | |
"^CNXENERGY": "NIFTY ENERGY", | |
"^CNXINFRA": "NIFTY INFRASTRUCTURE", | |
"^CNXMNC": "NIFTY MNC", | |
"^CNXPSE": "NIFTY PSE", | |
"^CNXSERVICE": "NIFTY SERVICES SECTOR", | |
"NIFTY100_ESG.NS": "NIFTY100 ESG SECTOR LEADERS", | |
} | |
def _getBacktestDate(self, backtest): | |
try: | |
end = backtest + datetime.timedelta(days=1) | |
if "d" in self.configManager.period: | |
delta = datetime.timedelta(days = self.configManager.getPeriodNumeric()) | |
elif "wk" in self.configManager.period: | |
delta = datetime.timedelta(days = self.configManager.getPeriodNumeric() * 7) | |
elif "m" in self.configManager.period: | |
delta = datetime.timedelta(minutes = self.configManager.getPeriodNumeric()) | |
elif "h" in self.configManager.period: | |
delta = datetime.timedelta(hours = self.configManager.getPeriodNumeric()) | |
start = end - delta | |
return [start, end] | |
except: | |
return [None, None] | |
def _getDatesForBacktestReport(self, backtest): | |
dateDict = {} | |
try: | |
today = datetime.date.today() | |
dateDict['T+1d'] = backtest + datetime.timedelta(days=1) if backtest + datetime.timedelta(days=1) < today else None | |
dateDict['T+1wk'] = backtest + datetime.timedelta(weeks=1) if backtest + datetime.timedelta(weeks=1) < today else None | |
dateDict['T+1mo'] = backtest + datetime.timedelta(days=30) if backtest + datetime.timedelta(days=30) < today else None | |
dateDict['T+6mo'] = backtest + datetime.timedelta(days=180) if backtest + datetime.timedelta(days=180) < today else None | |
dateDict['T+1y'] = backtest + datetime.timedelta(days=365) if backtest + datetime.timedelta(days=365) < today else None | |
for key, val in dateDict.copy().items(): | |
if val is not None: | |
if val.weekday() == 5: # 5 is Saturday, 6 is Sunday | |
adjusted_date = val + datetime.timedelta(days=2) | |
dateDict[key] = adjusted_date | |
elif val.weekday() == 6: | |
adjusted_date = val + datetime.timedelta(days=1) | |
dateDict[key] = adjusted_date | |
except: | |
pass | |
return dateDict | |
def fetchCodes(self, tickerOption,proxyServer=None): | |
listStockCodes = [] | |
if tickerOption == 12: | |
url = "https://archives.nseindia.com/content/equities/EQUITY_L.csv" | |
return list(pd.read_csv(url)['SYMBOL'].values) | |
if tickerOption == 15: | |
return ["MMM", "ABT", "ABBV", "ABMD", "ACN", "ATVI", "ADBE", "AMD", "AAP", "AES", "AFL", "A", "APD", "AKAM", "ALK", "ALB", "ARE", "ALXN", "ALGN", "ALLE", "AGN", "ADS", "LNT", "ALL", "GOOGL", "GOOG", "MO", "AMZN", "AMCR", "AEE", "AAL", "AEP", "AXP", "AIG", "AMT", "AWK", "AMP", "ABC", "AME", "AMGN", "APH", "ADI", "ANSS", "ANTM", "AON", "AOS", "APA", "AIV", "AAPL", "AMAT", "APTV", "ADM", "ARNC", "ANET", "AJG", "AIZ", "ATO", "T", "ADSK", "ADP", "AZO", "AVB", "AVY", "BKR", "BLL", "BAC", "BK", "BAX", "BDX", "BRK.B", "BBY", "BIIB", "BLK", "BA", "BKNG", "BWA", "BXP", "BSX", "BMY", "AVGO", "BR", "BF.B", "CHRW", "COG", "CDNS", "CPB", "COF", "CPRI", "CAH", "KMX", "CCL", "CAT", "CBOE", "CBRE", "CDW", "CE", "CNC", "CNP", "CTL", "CERN", "CF", "SCHW", "CHTR", "CVX", "CMG", "CB", "CHD", "CI", "XEC", "CINF", "CTAS", "CSCO", "C", "CFG", "CTXS", "CLX", "CME", "CMS", "KO", "CTSH", "CL", "CMCSA", "CMA", "CAG", "CXO", "COP", "ED", "STZ", "COO", "CPRT", "GLW", "CTVA", "COST", "COTY", "CCI", "CSX", "CMI", "CVS", "DHI", "DHR", "DRI", "DVA", "DE", "DAL", "XRAY", "DVN", "FANG", "DLR", "DFS", "DISCA", "DISCK", "DISH", "DG", "DLTR", "D", "DOV", "DOW", "DTE", "DUK", "DRE", "DD", "DXC", "ETFC", "EMN", "ETN", "EBAY", "ECL", "EIX", "EW", "EA", "EMR", "ETR", "EOG", "EFX", "EQIX", "EQR", "ESS", "EL", "EVRG", "ES", "RE", "EXC", "EXPE", "EXPD", "EXR", "XOM", "FFIV", "FB", "FAST", "FRT", "FDX", "FIS", "FITB", "FE", "FRC", "FISV", "FLT", "FLIR", "FLS", "FMC", "F", "FTNT", "FTV", "FBHS", "FOXA", "FOX", "BEN", "FCX", "GPS", "GRMN", "IT", "GD", "GE", "GIS", "GM", "GPC", "GILD", "GL", "GPN", "GS", "GWW", "HRB", "HAL", "HBI", "HOG", "HIG", "HAS", "HCA", "PEAK", "HP", "HSIC", "HSY", "HES", "HPE", "HLT", "HFC", "HOLX", "HD", "HON", "HRL", "HST", "HPQ", "HUM", "HBAN", "HII", "IEX", "IDXX", "INFO", "ITW", "ILMN", "IR", "INTC", "ICE", "IBM", "INCY", "IP", "IPG", "IFF", "INTU", "ISRG", "IVZ", "IPGP", "IQV", "IRM", "JKHY", "J", "JBHT", "SJM", "JNJ", "JCI", "JPM", "JNPR", "KSU", "K", "KEY", "KEYS", "KMB", "KIM", "KMI", "KLAC", "KSS", "KHC", "KR", "LB", "LHX", "LH", "LRCX", "LW", "LVS", "LEG", "LDOS", "LEN", "LLY", "LNC", "LIN", "LYV", "LKQ", "LMT", "L", "LOW", "LYB", "MTB", "M", "MRO", "MPC", "MKTX", "MAR", "MMC", "MLM", "MAS", "MA", "MKC", "MXIM", "MCD", "MCK", "MDT", "MRK", "MET", "MTD", "MGM", "MCHP", "MU", "MSFT", "MAA", "MHK", "TAP", "MDLZ", "MNST", "MCO", "MS", "MOS", "MSI", "MSCI", "MYL", "NDAQ", "NOV", "NTAP", "NFLX", "NWL", "NEM", "NWSA", "NWS", "NEE", "NLSN", "NKE", "NI", "NBL", "JWN", "NSC", "NTRS", "NOC", "NLOK", "NCLH", "NRG", "NUE", "NVDA", "NVR", "ORLY", "OXY", "ODFL", "OMC", "OKE", "ORCL", "PCAR", "PKG", "PH", "PAYX", "PYPL", "PNR", "PBCT", "PEP", "PKI", "PRGO", "PFE", "PM", "PSX", "PNW", "PXD", "PNC", "PPG", "PPL", "PFG", "PG", "PGR", "PLD", "PRU", "PEG", "PSA", "PHM", "PVH", "QRVO", "PWR", "QCOM", "DGX", "RL", "RJF", "RTN", "O", "REG", "REGN", "RF", "RSG", "RMD", "RHI", "ROK", "ROL", "ROP", "ROST", "RCL", "SPGI", "CRM", "SBAC", "SLB", "STX", "SEE", "SRE", "NOW", "SHW", "SPG", "SWKS", "SLG", "SNA", "SO", "LUV", "SWK", "SBUX", "STT", "STE", "SYK", "SIVB", "SYF", "SNPS", "SYY", "TMUS", "TROW", "TTWO", "TPR", "TGT", "TEL", "FTI", "TFX", "TXN", "TXT", "TMO", "TIF", "TJX", "TSCO", "TDG", "TRV", "TFC", "TWTR", "TSN", "UDR", "ULTA", "USB", "UAA", "UA", "UNP", "UAL", "UNH", "UPS", "URI", "UTX", "UHS", "UNM", "VFC", "VLO", "VAR", "VTR", "VRSN", "VRSK", "VZ", "VRTX", "VIAC", "V", "VNO", "VMC", "WRB", "WAB", "WMT", "WBA", "DIS", "WM", "WAT", "WEC", "WCG", "WFC", "WELL", "WDC", "WU", "WRK", "WY", "WHR", "WMB", "WLTW", "WYNN", "XEL", "XRX", "XLNX", "XYL", "YUM", "ZBRA", "ZBH", "ZION", "ZTS"] | |
if tickerOption == 16: | |
return self.getAllNiftyIndices() | |
tickerMapping = { | |
1: "https://archives.nseindia.com/content/indices/ind_nifty50list.csv", | |
2: "https://archives.nseindia.com/content/indices/ind_niftynext50list.csv", | |
3: "https://archives.nseindia.com/content/indices/ind_nifty100list.csv", | |
4: "https://archives.nseindia.com/content/indices/ind_nifty200list.csv", | |
5: "https://archives.nseindia.com/content/indices/ind_nifty500list.csv", | |
6: "https://archives.nseindia.com/content/indices/ind_niftysmallcap50list.csv", | |
7: "https://archives.nseindia.com/content/indices/ind_niftysmallcap100list.csv", | |
8: "https://archives.nseindia.com/content/indices/ind_niftysmallcap250list.csv", | |
9: "https://archives.nseindia.com/content/indices/ind_niftymidcap50list.csv", | |
10: "https://archives.nseindia.com/content/indices/ind_niftymidcap100list.csv", | |
11: "https://archives.nseindia.com/content/indices/ind_niftymidcap150list.csv", | |
14: "https://archives.nseindia.com/content/fo/fo_mktlots.csv" | |
} | |
url = tickerMapping.get(tickerOption) | |
try: | |
if proxyServer: | |
res = requests.get(url,proxies={'https':proxyServer}) | |
else: | |
res = requests.get(url) | |
cr = csv.reader(res.text.strip().split('\n')) | |
if tickerOption == 14: | |
for i in range(5): | |
next(cr) # skipping first line | |
for row in cr: | |
listStockCodes.append(row[1].strip()) | |
else: | |
next(cr) # skipping first line | |
for row in cr: | |
listStockCodes.append(row[2]) | |
except Exception as error: | |
print(error) | |
return listStockCodes | |
# Fetch all stock codes from NSE | |
def fetchStockCodes(self, tickerOption, proxyServer=None): | |
listStockCodes = [] | |
if tickerOption == 0: | |
stockCode = None | |
while stockCode == None or stockCode == "": | |
stockCode = str(input(colorText.BOLD + colorText.BLUE + | |
"[+] Enter Stock Code(s) for screening (Multiple codes should be seperated by ,): ")).upper() | |
stockCode = stockCode.replace(" ", "") | |
listStockCodes = stockCode.split(',') | |
else: | |
print(colorText.BOLD + | |
"[+] Getting Stock Codes From NSE... ", end='') | |
listStockCodes = self.fetchCodes(tickerOption,proxyServer=proxyServer) | |
if type(listStockCodes) == dict: | |
listStockCodes = list(listStockCodes.keys()) | |
if len(listStockCodes) > 10: | |
print(colorText.GREEN + ("=> Done! Fetched %d stock codes." % | |
len(listStockCodes)) + colorText.END) | |
if self.configManager.shuffleEnabled: | |
random.shuffle(listStockCodes) | |
print(colorText.BLUE + | |
"[+] Stock shuffling is active." + colorText.END) | |
else: | |
print(colorText.FAIL + | |
"[+] Stock shuffling is inactive." + colorText.END) | |
if self.configManager.stageTwo: | |
print( | |
colorText.BLUE + "[+] Screening only for the stocks in Stage-2! Edit User Config to change this." + colorText.END) | |
else: | |
print( | |
colorText.FAIL + "[+] Screening only for the stocks in all Stages! Edit User Config to change this." + colorText.END) | |
else: | |
input( | |
colorText.FAIL + "=> Error getting stock codes from NSE! Press any key to exit!" + colorText.END) | |
sys.exit("Exiting script..") | |
return listStockCodes | |
# Fetch stock price data from Yahoo finance | |
def fetchStockData(self, stockCode, period, duration, proxyServer, screenResultsCounter, screenCounter, totalSymbols, backtestDate=None, printCounter=False, tickerOption=None): | |
dateDict = None | |
with SuppressOutput(suppress_stdout=True, suppress_stderr=True): | |
append_exchange = ".NS" | |
if tickerOption == 15 or tickerOption == 16: | |
append_exchange = "" | |
data = yf.download( | |
tickers=stockCode + append_exchange, | |
period=period, | |
interval=duration, | |
proxy=proxyServer, | |
progress=False, | |
timeout=10, | |
start=self._getBacktestDate(backtest=backtestDate)[0], | |
end=self._getBacktestDate(backtest=backtestDate)[1] | |
) | |
if backtestDate != datetime.date.today(): | |
dateDict = self._getDatesForBacktestReport(backtest=backtestDate) | |
backtestData = yf.download( | |
tickers=stockCode + append_exchange, | |
interval='1d', | |
proxy=proxyServer, | |
progress=False, | |
timeout=10, | |
start=backtestDate - datetime.timedelta(days=1), | |
end=backtestDate + datetime.timedelta(days=370) | |
) | |
for key, value in dateDict.copy().items(): | |
if value is not None: | |
try: | |
dateDict[key] = backtestData.loc[pd.Timestamp(value)]['Close'] | |
except KeyError: | |
continue | |
dateDict['T+52wkH'] = backtestData['High'].max() | |
dateDict['T+52wkL'] = backtestData['Low'].min() | |
if printCounter: | |
sys.stdout.write("\r\033[K") | |
try: | |
print(colorText.BOLD + colorText.GREEN + ("[%d%%] Screened %d, Found %d. Fetching data & Analyzing %s..." % ( | |
int((screenCounter.value/totalSymbols)*100), screenCounter.value, screenResultsCounter.value, stockCode)) + colorText.END, end='') | |
except ZeroDivisionError: | |
pass | |
if len(data) == 0: | |
print(colorText.BOLD + colorText.FAIL + | |
"=> Failed to fetch!" + colorText.END, end='\r', flush=True) | |
raise StockDataEmptyException | |
return None | |
print(colorText.BOLD + colorText.GREEN + "=> Done!" + | |
colorText.END, end='\r', flush=True) | |
return data, dateDict | |
# Get Daily Nifty 50 Index: | |
def fetchLatestNiftyDaily(self, proxyServer=None): | |
data = yf.download( | |
tickers="^NSEI", | |
period='5d', | |
interval='1d', | |
proxy=proxyServer, | |
progress=False, | |
timeout=10 | |
) | |
gold = yf.download( | |
tickers="GC=F", | |
period='5d', | |
interval='1d', | |
proxy=proxyServer, | |
progress=False, | |
timeout=10 | |
).add_prefix(prefix='gold_') | |
crude = yf.download( | |
tickers="CL=F", | |
period='5d', | |
interval='1d', | |
proxy=proxyServer, | |
progress=False, | |
timeout=10 | |
).add_prefix(prefix='crude_') | |
data = pd.concat([data, gold, crude], axis=1) | |
return data | |
# Get Data for Five EMA strategy | |
def fetchFiveEmaData(self, proxyServer=None): | |
nifty_sell = yf.download( | |
tickers="^NSEI", | |
period='5d', | |
interval='5m', | |
proxy=proxyServer, | |
progress=False, | |
timeout=10 | |
) | |
banknifty_sell = yf.download( | |
tickers="^NSEBANK", | |
period='5d', | |
interval='5m', | |
proxy=proxyServer, | |
progress=False, | |
timeout=10 | |
) | |
nifty_buy = yf.download( | |
tickers="^NSEI", | |
period='5d', | |
interval='15m', | |
proxy=proxyServer, | |
progress=False, | |
timeout=10 | |
) | |
banknifty_buy = yf.download( | |
tickers="^NSEBANK", | |
period='5d', | |
interval='15m', | |
proxy=proxyServer, | |
progress=False, | |
timeout=10 | |
) | |
return nifty_buy, banknifty_buy, nifty_sell, banknifty_sell | |
# Load stockCodes from the watchlist.xlsx | |
def fetchWatchlist(self): | |
createTemplate = False | |
data = pd.DataFrame() | |
try: | |
data = pd.read_excel('watchlist.xlsx') | |
except FileNotFoundError: | |
print(colorText.BOLD + colorText.FAIL + | |
f'[+] watchlist.xlsx not found in f{os.getcwd()}' + colorText.END) | |
createTemplate = True | |
try: | |
if not createTemplate: | |
data = data['Stock Code'].values.tolist() | |
except KeyError: | |
print(colorText.BOLD + colorText.FAIL + | |
'[+] Bad Watchlist Format: First Column (A1) should have Header named "Stock Code"' + colorText.END) | |
createTemplate = True | |
if createTemplate: | |
if isDocker(): | |
print(colorText.BOLD + colorText.FAIL + | |
f'[+] This feature is not available with dockerized application. Try downloading .exe/.bin file to use this!' + colorText.END) | |
return None | |
sample = {'Stock Code': ['SBIN', 'INFY', 'TATAMOTORS', 'ITC']} | |
sample_data = pd.DataFrame(sample, columns=['Stock Code']) | |
sample_data.to_excel('watchlist_template.xlsx', | |
index=False, header=True) | |
print(colorText.BOLD + colorText.BLUE + | |
f'[+] watchlist_template.xlsx created in {os.getcwd()} as a referance template.' + colorText.END) | |
return None | |
return data | |