| | |
| |
|
| | |
| | |
| |
|
| | |
| | import os |
| | os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' |
| | import platform |
| | import sys |
| | import classes.Fetcher as Fetcher |
| | import classes.ConfigManager as ConfigManager |
| | import classes.Screener as Screener |
| | import classes.Utility as Utility |
| | from classes.ColorText import colorText |
| | from classes.OtaUpdater import OTAUpdater |
| | from classes.CandlePatterns import CandlePatterns |
| | from classes.ParallelProcessing import StockConsumer |
| | from classes.Changelog import VERSION |
| | from classes.Utility import isDocker, isGui |
| | from alive_progress import alive_bar |
| | import argparse |
| | import urllib |
| | import numpy as np |
| | import pandas as pd |
| | from datetime import datetime, date |
| | from time import sleep |
| | from tabulate import tabulate |
| | import multiprocessing |
| | multiprocessing.freeze_support() |
| | try: |
| | import chromadb |
| | CHROMA_AVAILABLE = True |
| | except: |
| | CHROMA_AVAILABLE = False |
| |
|
| | |
| | argParser = argparse.ArgumentParser() |
| | argParser.add_argument('-t', '--testbuild', action='store_true', help='Run in test-build mode', required=False) |
| | argParser.add_argument('-d', '--download', action='store_true', help='Only Download Stock data in .pkl file', required=False) |
| | argParser.add_argument('-v', action='store_true') |
| | args = argParser.parse_args() |
| |
|
| | |
| | TEST_STKCODE = "SBIN" |
| |
|
| | |
| | np.seterr(divide='ignore', invalid='ignore') |
| |
|
| | |
| | screenCounter = None |
| | screenResultsCounter = None |
| | stockDict = None |
| | keyboardInterruptEvent = None |
| | loadedStockData = False |
| | loadCount = 0 |
| | maLength = None |
| | newlyListedOnly = False |
| | vectorSearch = False |
| |
|
| | CHROMADB_PATH = "chromadb_store/" |
| |
|
| | configManager = ConfigManager.tools() |
| | fetcher = Fetcher.tools(configManager) |
| | screener = Screener.tools(configManager) |
| | candlePatterns = CandlePatterns() |
| |
|
| | |
| | try: |
| | proxyServer = urllib.request.getproxies()['http'] |
| | except KeyError: |
| | proxyServer = "" |
| |
|
| | |
| | if CHROMA_AVAILABLE: |
| | chroma_client = chromadb.PersistentClient(path=CHROMADB_PATH) |
| | try: |
| | chroma_client.delete_collection("nse_stocks") |
| | except: |
| | pass |
| |
|
| |
|
| | |
| |
|
| |
|
| | def initExecution(): |
| | global newlyListedOnly |
| | print(colorText.BOLD + colorText.WARN + |
| | '[+] Select an Index for Screening: ' + colorText.END) |
| | print(colorText.BOLD + ''' |
| | W > Screen stocks from my own Watchlist |
| | N > Nifty Prediction using Artifical Intelligence (Use for Gap-Up/Gap-Down/BTST/STBT) |
| | E > Live Index Scan : 5 EMA for Intraday |
| | S > Search for Similar Stocks (forming Similar Chart Pattern) |
| | |
| | 0 > Screen stocks by the stock names (NSE Stock Code) |
| | 1 > Nifty 50 2 > Nifty Next 50 3 > Nifty 100 |
| | 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 16 > Sectoral Indices (NSE) |
| | Enter > All Stocks (default) ''' + colorText.END |
| | ) |
| | try: |
| | tickerOption = input( |
| | colorText.BOLD + colorText.FAIL + '[+] Select option: ') |
| | print(colorText.END, end='') |
| | if tickerOption == '': |
| | tickerOption = 12 |
| | |
| | elif not tickerOption.isnumeric(): |
| | tickerOption = tickerOption.upper() |
| | else: |
| | tickerOption = int(tickerOption) |
| | if(tickerOption < 0 or tickerOption > 16): |
| | raise ValueError |
| | elif tickerOption == 13: |
| | newlyListedOnly = True |
| | tickerOption = 12 |
| | except KeyboardInterrupt: |
| | raise KeyboardInterrupt |
| | except Exception as e: |
| | print(colorText.BOLD + colorText.FAIL + |
| | '\n[+] Please enter a valid numeric option & Try Again!' + colorText.END) |
| | sleep(2) |
| | Utility.tools.clearScreen() |
| | return initExecution() |
| |
|
| | if tickerOption == 'N' or tickerOption == 'E' or tickerOption == 'S': |
| | return tickerOption, 0 |
| |
|
| | if tickerOption and tickerOption != 'W': |
| | print(colorText.BOLD + colorText.WARN + |
| | '\n[+] Select a Critera for Stock Screening: ' + colorText.END) |
| | print(colorText.BOLD + ''' |
| | 0 > Full Screening (Shows Technical Parameters without Any Criteria) |
| | 1 > Screen stocks for Breakout or Consolidation |
| | 2 > Screen for the stocks with recent Breakout & Volume |
| | 3 > Screen for the Consolidating stocks |
| | 4 > Screen for the stocks with Lowest Volume in last 'N'-days (Early Breakout Detection) |
| | 5 > Screen for the stocks with RSI |
| | 6 > Screen for the stocks showing Reversal Signals |
| | 7 > Screen for the stocks making Chart Patterns |
| | 8 > Edit user configuration |
| | 9 > Show user configuration |
| | 10 > Show Last Screened Results |
| | 11 > Help / About Developer |
| | 12 > Exit''' + colorText.END |
| | ) |
| | try: |
| | if tickerOption and tickerOption != 'W': |
| | executeOption = input( |
| | colorText.BOLD + colorText.FAIL + '[+] Select option: ') |
| | print(colorText.END, end='') |
| | if executeOption == '': |
| | executeOption = 0 |
| | executeOption = int(executeOption) |
| | if(executeOption < 0 or executeOption > 14): |
| | raise ValueError |
| | else: |
| | executeOption = 0 |
| | except KeyboardInterrupt: |
| | raise KeyboardInterrupt |
| | except Exception as e: |
| | print(colorText.BOLD + colorText.FAIL + |
| | '\n[+] Please enter a valid numeric option & Try Again!' + colorText.END) |
| | sleep(2) |
| | Utility.tools.clearScreen() |
| | return initExecution() |
| | return tickerOption, executeOption |
| |
|
| | |
| | def main(testing=False, testBuild=False, downloadOnly=False, execute_inputs:list = [], isDevVersion=None, backtestDate=date.today()): |
| | global screenCounter, screenResultsCounter, stockDict, loadedStockData, keyboardInterruptEvent, loadCount, maLength, newlyListedOnly, vectorSearch |
| | screenCounter = multiprocessing.Value('i', 1) |
| | screenResultsCounter = multiprocessing.Value('i', 0) |
| | keyboardInterruptEvent = multiprocessing.Manager().Event() |
| |
|
| | if stockDict is None or Utility.tools.isBacktesting(backtestDate=backtestDate): |
| | stockDict = multiprocessing.Manager().dict() |
| | loadCount = 0 |
| |
|
| | minRSI = 0 |
| | maxRSI = 100 |
| | insideBarToLookback = 7 |
| | respChartPattern = 1 |
| | daysForLowestVolume = 30 |
| | reversalOption = None |
| |
|
| | screenResults = pd.DataFrame(columns=[ |
| | 'Stock', 'Consolidating', 'Breaking-Out', 'LTP', 'Volume', 'MA-Signal', 'RSI', 'Trend', 'Pattern']) |
| | saveResults = pd.DataFrame(columns=[ |
| | 'Stock', 'Consolidating', 'Breaking-Out', 'LTP', 'Volume', 'MA-Signal', 'RSI', 'Trend', 'Pattern']) |
| |
|
| | |
| | if testBuild: |
| | tickerOption, executeOption = 1, 0 |
| | elif downloadOnly: |
| | tickerOption, executeOption = 12, 2 |
| | else: |
| | try: |
| | if execute_inputs != []: |
| | if not configManager.checkConfigFile(): |
| | configManager.setConfig(ConfigManager.parser, default=True, showFileCreatedText=False) |
| | try: |
| | tickerOption, executeOption = int(execute_inputs[0]), int(execute_inputs[1]) |
| | if tickerOption == 0: |
| | stockCode = execute_inputs[2].replace(" ", "") |
| | listStockCodes = stockCode.split(',') |
| | except: |
| | tickerOption, executeOption = str(execute_inputs[0]), int(execute_inputs[1]) |
| | if tickerOption == 13: |
| | newlyListedOnly = True |
| | tickerOption = 12 |
| | else: |
| | tickerOption, executeOption = initExecution() |
| | except KeyboardInterrupt: |
| | if execute_inputs == [] and not isGui(): |
| | input(colorText.BOLD + colorText.FAIL + |
| | "[+] Press any key to Exit!" + colorText.END) |
| | sys.exit(0) |
| |
|
| | if executeOption == 4: |
| | try: |
| | if execute_inputs != []: |
| | daysForLowestVolume = int(execute_inputs[2]) |
| | else: |
| | daysForLowestVolume = int(input(colorText.BOLD + colorText.WARN + |
| | '\n[+] The Volume should be lowest since last how many candles? ')) |
| | except ValueError: |
| | print(colorText.END) |
| | print(colorText.BOLD + colorText.FAIL + |
| | '[+] Error: Non-numeric value entered! Screening aborted.' + colorText.END) |
| | if not isGui(): |
| | input('') |
| | main() |
| | print(colorText.END) |
| | if executeOption == 5: |
| | if execute_inputs != []: |
| | minRSI, maxRSI = int(execute_inputs[2]), int(execute_inputs[3]) |
| | else: |
| | minRSI, maxRSI = Utility.tools.promptRSIValues() |
| | if (not minRSI and not maxRSI): |
| | print(colorText.BOLD + colorText.FAIL + |
| | '\n[+] Error: Invalid values for RSI! Values should be in range of 0 to 100. Screening aborted.' + colorText.END) |
| | if not isGui(): |
| | input('') |
| | main() |
| | if executeOption == 6: |
| | if execute_inputs != []: |
| | reversalOption = int(execute_inputs[2]) |
| | try: |
| | maLength = int(execute_inputs[3]) |
| | except ValueError: |
| | pass |
| | else: |
| | reversalOption, maLength = Utility.tools.promptReversalScreening() |
| | if reversalOption is None or reversalOption == 0: |
| | if not isGui(): |
| | main() |
| | if executeOption == 7: |
| | if execute_inputs != []: |
| | respChartPattern = int(execute_inputs[2]) |
| | try: |
| | insideBarToLookback = float(execute_inputs[3]) |
| | except ValueError: |
| | pass |
| | else: |
| | respChartPattern, insideBarToLookback = Utility.tools.promptChartPatterns() |
| | if insideBarToLookback is None: |
| | if not isGui(): |
| | main() |
| | if executeOption == 8: |
| | configManager.setConfig(ConfigManager.parser) |
| | if not isGui(): |
| | main() |
| | if executeOption == 9: |
| | configManager.showConfigFile() |
| | if not isGui(): |
| | main() |
| | if executeOption == 10: |
| | Utility.tools.getLastScreenedResults() |
| | if not isGui(): |
| | main() |
| | if executeOption == 11: |
| | Utility.tools.showDevInfo() |
| | if not isGui(): |
| | main() |
| | if executeOption == 12: |
| | if not isGui(): |
| | input(colorText.BOLD + colorText.FAIL + |
| | "[+] Press any key to Exit!" + colorText.END) |
| | sys.exit(0) |
| |
|
| | 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': |
| | listStockCodes = fetcher.fetchWatchlist() |
| | if listStockCodes is None: |
| | input(colorText.BOLD + colorText.FAIL + |
| | f'[+] Create the watchlist.xlsx file in {os.getcwd()} and Restart the Program!' + colorText.END) |
| | sys.exit(0) |
| | elif tickerOption == 'N': |
| | os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' |
| | import tensorflow as tf |
| | physical_devices = tf.config.list_physical_devices('GPU') |
| | try: |
| | tf.config.set_visible_devices([], 'GPU') |
| | visible_devices = tf.config.get_visible_devices() |
| | for device in visible_devices: |
| | assert device.device_type != 'GPU' |
| | except: |
| | pass |
| | prediction = screener.getNiftyPrediction( |
| | data=fetcher.fetchLatestNiftyDaily(proxyServer=proxyServer), |
| | proxyServer=proxyServer |
| | ) |
| | input('\nPress any key to Continue...\n') |
| | return |
| | elif tickerOption == 'E': |
| | result_df = pd.DataFrame(columns=['Time','Stock/Index','Action','SL','Target','R:R']) |
| | last_signal = {} |
| | first_scan = True |
| | result_df = screener.monitorFiveEma( |
| | proxyServer=proxyServer, |
| | fetcher=fetcher, |
| | result_df=result_df, |
| | last_signal=last_signal |
| | ) |
| | try: |
| | while True: |
| | Utility.tools.clearScreen() |
| | last_result_len = len(result_df) |
| | result_df = screener.monitorFiveEma( |
| | proxyServer=proxyServer, |
| | fetcher=fetcher, |
| | result_df=result_df, |
| | last_signal=last_signal |
| | ) |
| | print(colorText.BOLD + colorText.WARN + '[+] 5-EMA : Live Intraday Scanner \t' + colorText.END + colorText.FAIL + f'Last Scanned: {datetime.now().strftime("%H:%M:%S")}\n' + colorText.END) |
| | print(tabulate(result_df, headers='keys', tablefmt='psql')) |
| | print('\nPress Ctrl+C to exit.') |
| | if len(result_df) != last_result_len and not first_scan: |
| | Utility.tools.alertSound(beeps=5) |
| | sleep(60) |
| | first_scan = False |
| | except KeyboardInterrupt: |
| | if not isGui(): |
| | input('\nPress any key to Continue...\n') |
| | return |
| | elif tickerOption == 'S': |
| | if not CHROMA_AVAILABLE: |
| | print(colorText.BOLD + colorText.FAIL + |
| | "\n\n[+] ChromaDB not available in your environment! You can't use this feature!\n" + colorText.END) |
| | else: |
| | if execute_inputs != []: |
| | stockCode, candles = execute_inputs[2], execute_inputs[3] |
| | else: |
| | stockCode, candles = Utility.tools.promptSimilarStockSearch() |
| | vectorSearch = [stockCode, candles, True] |
| | tickerOption, executeOption = 12, 1 |
| | listStockCodes = fetcher.fetchStockCodes(tickerOption, proxyServer=proxyServer) |
| | else: |
| | if tickerOption == 14: |
| | configManager.stageTwo = False |
| | configManager.minLTP = 0.1 |
| | configManager.maxLTP = 999999999 |
| | if (execute_inputs != [] and tickerOption != 0) or execute_inputs == []: |
| | listStockCodes = fetcher.fetchStockCodes(tickerOption, proxyServer=proxyServer) |
| | except urllib.error.URLError: |
| | print(colorText.BOLD + colorText.FAIL + |
| | "\n\n[+] Oops! It looks like you don't have an Internet connectivity at the moment! Press any key to exit!" + colorText.END) |
| | if not isGui(): |
| | input('') |
| | sys.exit(0) |
| |
|
| | if not Utility.tools.isTradingTime() and configManager.cacheEnabled and not loadedStockData and not testing and not Utility.tools.isBacktesting(backtestDate=backtestDate): |
| | Utility.tools.loadStockData(stockDict, configManager, proxyServer) |
| | loadedStockData = True |
| | loadCount = len(stockDict) |
| |
|
| | print(colorText.BOLD + colorText.WARN + |
| | "[+] Starting Stock Screening.. Press Ctrl+C to stop!\n") |
| |
|
| | items = [(tickerOption, executeOption, reversalOption, maLength, daysForLowestVolume, minRSI, maxRSI, respChartPattern, insideBarToLookback, len(listStockCodes), |
| | configManager, fetcher, screener, candlePatterns, stock, newlyListedOnly, downloadOnly, vectorSearch, isDevVersion, backtestDate) |
| | for stock in listStockCodes] |
| |
|
| | tasks_queue = multiprocessing.JoinableQueue() |
| | results_queue = multiprocessing.Queue() |
| |
|
| | totalConsumers = multiprocessing.cpu_count() |
| | if totalConsumers == 1: |
| | totalConsumers = 2 |
| | if configManager.cacheEnabled is True and multiprocessing.cpu_count() > 2: |
| | totalConsumers -= 1 |
| | consumers = [StockConsumer(tasks_queue, results_queue, screenCounter, screenResultsCounter, stockDict, proxyServer, keyboardInterruptEvent) |
| | for _ in range(totalConsumers)] |
| |
|
| | for worker in consumers: |
| | worker.daemon = True |
| | worker.start() |
| |
|
| | if testing or testBuild: |
| | for item in items: |
| | tasks_queue.put(item) |
| | result = results_queue.get() |
| | if result is not None: |
| | screenResults = pd.concat([screenResults, pd.DataFrame([result[0]])], ignore_index=True) |
| | saveResults = pd.concat([saveResults, pd.DataFrame([result[1]])], ignore_index=True) |
| | if testing or (testBuild and len(screenResults) > 2): |
| | break |
| | else: |
| | for item in items: |
| | tasks_queue.put(item) |
| | |
| | for _ in range(multiprocessing.cpu_count()): |
| | tasks_queue.put(None) |
| | try: |
| | numStocks, totalStocks = len(listStockCodes), len(listStockCodes) |
| | os.environ['SCREENIPY_TOTAL_STOCKS'] = str(totalStocks) |
| | print(colorText.END+colorText.BOLD) |
| | bar, spinner = Utility.tools.getProgressbarStyle() |
| | with alive_bar(numStocks, bar=bar, spinner=spinner) as progressbar: |
| | while numStocks: |
| | result = results_queue.get() |
| | if result is not None: |
| | screenResults = pd.concat([screenResults, pd.DataFrame([result[0]])], ignore_index=True) |
| | saveResults = pd.concat([saveResults, pd.DataFrame([result[1]])], ignore_index=True) |
| | numStocks -= 1 |
| | os.environ['SCREENIPY_SCREEN_COUNTER'] = str(int((totalStocks-numStocks)/totalStocks*100)) |
| | progressbar.text(colorText.BOLD + colorText.GREEN + |
| | f'Found {screenResultsCounter.value} Stocks' + colorText.END) |
| | progressbar() |
| | except KeyboardInterrupt: |
| | try: |
| | keyboardInterruptEvent.set() |
| | except KeyboardInterrupt: |
| | pass |
| | print(colorText.BOLD + colorText.FAIL + |
| | "\n[+] Terminating Script, Please wait..." + colorText.END) |
| | for worker in consumers: |
| | worker.terminate() |
| |
|
| | print(colorText.END) |
| | |
| | for worker in consumers: |
| | try: |
| | worker.terminate() |
| | except OSError as e: |
| | if e.winerror == 5: |
| | pass |
| |
|
| | |
| | from queue import Empty |
| | while True: |
| | try: |
| | _ = tasks_queue.get(False) |
| | except Exception as e: |
| | break |
| |
|
| | if CHROMA_AVAILABLE and type(vectorSearch) == list and vectorSearch[2]: |
| | chroma_client = chromadb.PersistentClient(path=CHROMADB_PATH) |
| | collection = chroma_client.get_collection(name="nse_stocks") |
| | query_embeddings= collection.get(ids = [stockCode], include=["embeddings"])["embeddings"] |
| | results = collection.query( |
| | query_embeddings=query_embeddings, |
| | n_results=4 |
| | )['ids'][0] |
| | try: |
| | results.remove(stockCode) |
| | except ValueError: |
| | pass |
| | matchedScreenResults, matchedSaveResults = pd.DataFrame(columns=screenResults.columns), pd.DataFrame(columns=saveResults.columns) |
| | for stk in results: |
| | matchedScreenResults = pd.concat([matchedScreenResults, screenResults[screenResults['Stock'].str.contains(stk)]], ignore_index=True) |
| | matchedSaveResults = pd.concat([matchedSaveResults, saveResults[saveResults['Stock'].str.contains(stk)]], ignore_index=True) |
| | screenResults, saveResults = matchedScreenResults, matchedSaveResults |
| | |
| | screenResults.sort_values(by=['Stock'], ascending=True, inplace=True) |
| | saveResults.sort_values(by=['Stock'], ascending=True, inplace=True) |
| | screenResults.set_index('Stock', inplace=True) |
| | saveResults.set_index('Stock', inplace=True) |
| | screenResults.rename( |
| | columns={ |
| | 'Trend': f'Trend ({configManager.daysToLookback}Days)', |
| | 'Breaking-Out': f'Breakout ({configManager.daysToLookback}Days)', |
| | 'LTP': 'LTP (%% Chng)' |
| | }, |
| | inplace=True |
| | ) |
| | saveResults.rename( |
| | columns={ |
| | 'Trend': f'Trend ({configManager.daysToLookback}Days)', |
| | 'Breaking-Out': f'Breakout ({configManager.daysToLookback}Days)', |
| | }, |
| | inplace=True |
| | ) |
| | print(tabulate(screenResults, headers='keys', tablefmt='psql')) |
| |
|
| | print(colorText.BOLD + colorText.GREEN + |
| | f"[+] Found {len(screenResults)} Stocks." + colorText.END) |
| | if configManager.cacheEnabled and not Utility.tools.isTradingTime() and not testing and not Utility.tools.isBacktesting(backtestDate=backtestDate): |
| | print(colorText.BOLD + colorText.GREEN + |
| | "[+] Caching Stock Data for future use, Please Wait... " + colorText.END, end='') |
| | Utility.tools.saveStockData( |
| | stockDict, configManager, loadCount) |
| |
|
| | Utility.tools.setLastScreenedResults(screenResults) |
| | Utility.tools.setLastScreenedResults(saveResults, unformatted=True) |
| | if not testBuild and not downloadOnly: |
| | Utility.tools.promptSaveResults(saveResults) |
| | print(colorText.BOLD + colorText.WARN + |
| | "[+] Note: Trend calculation is based on number of days recent to screen as per your configuration." + colorText.END) |
| | print(colorText.BOLD + colorText.GREEN + |
| | "[+] Screening Completed! Press Enter to Continue.." + colorText.END) |
| | if not isGui(): |
| | input('') |
| | newlyListedOnly = False |
| | vectorSearch = False |
| |
|
| |
|
| | if __name__ == "__main__": |
| | Utility.tools.clearScreen() |
| | isDevVersion = OTAUpdater.checkForUpdate(proxyServer, VERSION) |
| | if not configManager.checkConfigFile(): |
| | configManager.setConfig(ConfigManager.parser, default=True, showFileCreatedText=False) |
| | if args.testbuild: |
| | print(colorText.BOLD + colorText.FAIL +"[+] Started in TestBuild mode!" + colorText.END) |
| | main(testBuild=True) |
| | elif args.download: |
| | print(colorText.BOLD + colorText.FAIL +"[+] Download ONLY mode! Stocks will not be screened!" + colorText.END) |
| | main(downloadOnly=True) |
| | else: |
| | try: |
| | while True: |
| | main() |
| | except Exception as e: |
| | raise e |
| | if isDevVersion == OTAUpdater.developmentVersion: |
| | raise(e) |
| | input(colorText.BOLD + colorText.FAIL + |
| | "[+] Press any key to Exit!" + colorText.END) |
| | sys.exit(1) |
| |
|