From 67ed922ee66fa1cc38682f49bb9d691f1f5f312e Mon Sep 17 00:00:00 2001 From: pranjal-joshi Date: Wed, 29 Nov 2023 20:32:23 +0530 Subject: [PATCH] v2.19 - Sectoral Indices added to Criteria based Screening --- src/classes/Changelog.py | 6 +++- src/classes/Fetcher.py | 50 ++++++++++++++++++++++++++++++- src/classes/ParallelProcessing.py | 12 ++++++-- src/release.md | 10 +++---- src/screenipy.py | 7 ++--- src/streamlit_app.py | 16 ++++++++-- 6 files changed, 86 insertions(+), 15 deletions(-) diff --git a/src/classes/Changelog.py b/src/classes/Changelog.py index 4fc00db1..0c667aa5 100644 --- a/src/classes/Changelog.py +++ b/src/classes/Changelog.py @@ -7,7 +7,7 @@ from classes.ColorText import colorText -VERSION = "2.18" +VERSION = "2.19" changelog = colorText.BOLD + '[ChangeLog]\n' + colorText.END + colorText.BLUE + ''' [1.00 - Beta] @@ -281,4 +281,8 @@ [2.18] 1. Critical backtest bug fixed (dropna axis-1 removed from results) 2. Clear stock cached data button added + +[2.19] +1. New Index (Group of Indices) `16 > Sectoral Indices` added + ''' + colorText.END diff --git a/src/classes/Fetcher.py b/src/classes/Fetcher.py index 2a176472..a6891ade 100644 --- a/src/classes/Fetcher.py +++ b/src/classes/Fetcher.py @@ -36,6 +36,50 @@ 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) @@ -80,6 +124,8 @@ def fetchCodes(self, tickerOption,proxyServer=None): 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", @@ -133,6 +179,8 @@ def fetchStockCodes(self, tickerOption, proxyServer=None): 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) @@ -162,7 +210,7 @@ def fetchStockData(self, stockCode, period, duration, proxyServer, screenResults dateDict = None with SuppressOutput(suppress_stdout=True, suppress_stderr=True): append_exchange = ".NS" - if tickerOption == 15: + if tickerOption == 15 or tickerOption == 16: append_exchange = "" data = yf.download( tickers=stockCode + append_exchange, diff --git a/src/classes/ParallelProcessing.py b/src/classes/ParallelProcessing.py index 9ce8d7bd..0d2f4877 100644 --- a/src/classes/ParallelProcessing.py +++ b/src/classes/ParallelProcessing.py @@ -18,6 +18,7 @@ import classes.Fetcher as Fetcher import classes.Screener as Screener import classes.Utility as Utility +from copy import deepcopy from classes.CandlePatterns import CandlePatterns from classes.ColorText import colorText from classes.SuppressOutput import SuppressOutput @@ -124,11 +125,18 @@ def screenStocks(self, tickerOption, executeOption, reversalOption, maLength, da with self.screenCounter.get_lock(): self.screenCounter.value += 1 if not processedData.empty: + urlStock = None + if tickerOption == 16: + urlStock = deepcopy(stock).replace('^','').replace('.NS','') + stock = fetcher.getAllNiftyIndices()[stock] + stock = stock.replace('^','').replace('.NS','') + urlStock = stock.replace('&','_') if urlStock is None else urlStock.replace('&','_') screeningDictionary['Stock'] = colorText.BOLD + \ - colorText.BLUE + f'\x1B]8;;https://in.tradingview.com/chart?symbol=NSE%3A{stock}\x1B\\{stock}\x1B]8;;\x1B\\' + colorText.END if tickerOption != 15 \ + colorText.BLUE + f'\x1B]8;;https://in.tradingview.com/chart?symbol=NSE%3A{urlStock}\x1B\\{stock}\x1B]8;;\x1B\\' + colorText.END if tickerOption < 15 \ else colorText.BOLD + \ - colorText.BLUE + f'\x1B]8;;https://in.tradingview.com/chart?symbol={stock}\x1B\\{stock}\x1B]8;;\x1B\\' + colorText.END + colorText.BLUE + f'\x1B]8;;https://in.tradingview.com/chart?symbol={urlStock}\x1B\\{stock}\x1B]8;;\x1B\\' + colorText.END saveDictionary['Stock'] = stock + consolidationValue = screener.validateConsolidation( processedData, screeningDictionary, saveDictionary, percentage=configManager.consolidationPercentage) isMaReversal = screener.validateMovingAverages( diff --git a/src/release.md b/src/release.md index b3ea26f3..6e9a481f 100644 --- a/src/release.md +++ b/src/release.md @@ -7,11 +7,11 @@ Screeni-py is now on **YouTube** for additional help! - Thank You for your suppo ⚠️ **Executable files (.exe, .bin and .run) are now DEPRECATED! Please Switch to Docker** -1. **Backtesting Reports** Added for Screening Patterns to Develope and Test Strategies! -2. **Position Size Calculator** tab added for Better and Quick Risk Management! -3. **Lorentzian Classification** (by @jdehorty) added for enhanced accuracy for your trades - - Try `Option > 6 > 7` 🤯 -4. **Artificial Intelligence v3 for Nifty 50 Prediction** - Predict Next day Gap-up/down using Nifty, Gold and Crude prices! - Try `Select Index for Screening > N` -5. **US S&P 500** Index added for scanning US markets. +1. **NSE Indices** added to find Sectoral opportunities - Try Index `16 > Sectoral Indices` +2. **Backtesting Reports** Added for Screening Patterns to Develope and Test Strategies! +3. **Position Size Calculator** tab added for Better and Quick Risk Management! +4. **Lorentzian Classification** (invented by Justin Dehorty) added for enhanced accuracy for your trades - - Try `Option > 6 > 7` 🤯 +5. **Artificial Intelligence v3 for Nifty 50 Prediction** - Predict Next day Gap-up/down using Nifty, Gold and Crude prices! - Try `Select Index for Screening > N` 6. **Search Similar Stocks** Added using Vector Similarity search - Try `Search Similar Stocks`. 7. New Screener **Buy at Trendline** added for Swing/Mid/Long term traders - Try `Option > 7 > 5`. diff --git a/src/screenipy.py b/src/screenipy.py index 61a161b2..a7f02c90 100644 --- a/src/screenipy.py +++ b/src/screenipy.py @@ -98,7 +98,7 @@ def initExecution(): 4 > Nifty 200 5 > Nifty 500 6 > Nifty Smallcap 50 7 > Nifty Smallcap 100 8 > Nifty Smallcap 250 9 > Nifty Midcap 50 10 > Nifty Midcap 100 11 > Nifty Midcap 150 13 > Newly Listed (IPOs in last 2 Year) - 14 > F&O Stocks Only 15 > US S&P 500 + 14 > F&O Stocks Only 15 > US S&P 500 16 > Sectoral Indices (NSE) Enter > All Stocks (default) ''' + colorText.END ) try: @@ -112,7 +112,7 @@ def initExecution(): tickerOption = tickerOption.upper() else: tickerOption = int(tickerOption) - if(tickerOption < 0 or tickerOption > 15): + if(tickerOption < 0 or tickerOption > 16): raise ValueError elif tickerOption == 13: newlyListedOnly = True @@ -292,8 +292,7 @@ def main(testing=False, testBuild=False, downloadOnly=False, execute_inputs:list "[+] Press any key to Exit!" + colorText.END) sys.exit(0) - print(tickerOption) - if tickerOption == 'W' or tickerOption == 'N' or tickerOption == 'E' or tickerOption == 'S' or (tickerOption >= 0 and tickerOption < 16): + if tickerOption == 'W' or tickerOption == 'N' or tickerOption == 'E' or tickerOption == 'S' or (tickerOption >= 0 and tickerOption < 17): configManager.getConfig(ConfigManager.parser) try: if tickerOption == 'W': diff --git a/src/streamlit_app.py b/src/streamlit_app.py index d2446889..ea52b9b3 100644 --- a/src/streamlit_app.py +++ b/src/streamlit_app.py @@ -13,6 +13,7 @@ from math import floor import classes.ConfigManager as ConfigManager import classes.Utility as Utility +import classes.Fetcher as Fetcher st.set_page_config(layout="wide", page_title="Screeni-py", page_icon="📈") @@ -62,9 +63,19 @@ def show_df_as_result_table(): type='secondary', use_container_width=True ) - if execute_inputs[0] != '15': + if int(execute_inputs[0]) < 15: df.index = df.index.map(lambda x: "https://in.tradingview.com/chart?symbol=NSE%3A" + x) df.index = df.index.map(lambda x: f'{x.split("%3A")[-1]}') + elif execute_inputs[0] == '16': + try: + fetcher = Fetcher.tools(configManager=ConfigManager.tools()) + url_dict_reversed = {key.replace('^','').replace('.NS',''): value for key, value in fetcher.getAllNiftyIndices().items()} + url_dict_reversed = {v: k for k, v in url_dict_reversed.items()} + df.index = df.index.map(lambda x: "https://in.tradingview.com/chart?symbol=NSE%3A" + url_dict_reversed[x]) + url_dict_reversed = {v: k for k, v in url_dict_reversed.items()} + df.index = df.index.map(lambda x: f'{url_dict_reversed[x.split("%3A")[-1]]}') + except KeyError: + pass else: df.index = df.index.map(lambda x: "https://in.tradingview.com/chart?symbol=" + x) df.index = df.index.map(lambda x: f'{x.split("=")[-1]}') @@ -310,7 +321,8 @@ def get_extra_inputs(tickerOption, executeOption, c_index=None, c_criteria=None, '11 > Nifty Midcap 150', '13 > Newly Listed (IPOs in last 2 Year)', '14 > F&O Stocks Only', - '15 > US S&P 500' + '15 > US S&P 500', + '16 > Sectoral Indices (NSE)' ] list_criteria = [