diff --git a/src/classes/Changelog.py b/src/classes/Changelog.py index 4966f75..1dec895 100644 --- a/src/classes/Changelog.py +++ b/src/classes/Changelog.py @@ -7,7 +7,7 @@ from classes.ColorText import colorText -VERSION = "1.17" +VERSION = "1.18" changelog = colorText.BOLD + '[ChangeLog]\n' + colorText.END + colorText.BLUE + ''' [1.00 - Beta] @@ -93,5 +93,9 @@ 2. Progressbar added. 3. Watchlist creation in Excel file and its screening. +[1.18] +1. Cache and Performance fixes. +2. Breakout Calculation Enhanced. + --- END --- ''' + colorText.END diff --git a/src/classes/Screener.py b/src/classes/Screener.py index b5a11b5..215ca32 100644 --- a/src/classes/Screener.py +++ b/src/classes/Screener.py @@ -176,9 +176,6 @@ def findBreakout(self, data, screenDict, saveDict, daysToLookback): data = data.fillna(0) data = data.replace([np.inf, -np.inf], 0) recent = data.head(1) - # Consider only for Green Candle - if not self.getCandleType(recent): - return False data = data[1:] hs = round(data.describe()['High']['max'],2) hc = round(data.describe()['Close']['max'],2) @@ -188,7 +185,7 @@ def findBreakout(self, data, screenDict, saveDict, daysToLookback): saveDict['Breaking-Out'] = str(hc) if rc >= hc: screenDict['Breaking-Out'] = colorText.BOLD + colorText.GREEN + "BO: " + str(hc) + " R: " + str(hs) + colorText.END - return True + return True and self.getCandleType(recent) screenDict['Breaking-Out'] = colorText.BOLD + colorText.FAIL + "BO: " + str(hc) + " R: " + str(hs) + colorText.END return False noOfHigherShadows = len(data[data.High > hc]) @@ -196,20 +193,20 @@ def findBreakout(self, data, screenDict, saveDict, daysToLookback): saveDict['Breaking-Out'] = str(hs) if rc >= hs: screenDict['Breaking-Out'] = colorText.BOLD + colorText.GREEN + "BO: " + str(hs) + colorText.END - return True + return True and self.getCandleType(recent) screenDict['Breaking-Out'] = colorText.BOLD + colorText.FAIL + "BO: " + str(hs) + colorText.END return False saveDict['Breaking-Out'] = str(hc) + ", " + str(hs) if rc >= hc: screenDict['Breaking-Out'] = colorText.BOLD + colorText.GREEN + "BO: " + str(hc) + " R: " + str(hs) + colorText.END - return True + return True and self.getCandleType(recent) screenDict['Breaking-Out'] = colorText.BOLD + colorText.FAIL + "BO: " + str(hc) + " R: " + str(hs) + colorText.END return False else: saveDict['Breaking-Out'] = str(hc) if rc >= hc: screenDict['Breaking-Out'] = colorText.BOLD + colorText.GREEN + "BO: " + str(hc) + colorText.END - return True + return True and self.getCandleType(recent) screenDict['Breaking-Out'] = colorText.BOLD + colorText.FAIL + "BO: " + str(hc) + colorText.END return False diff --git a/src/classes/Utility.py b/src/classes/Utility.py index 833e7dd..cf1d1aa 100644 --- a/src/classes/Utility.py +++ b/src/classes/Utility.py @@ -34,6 +34,8 @@ lastScreened = 'last_screened_results.pkl' # Class for managing misc and utility methods + + class tools: def clearScreen(): @@ -46,11 +48,16 @@ def clearScreen(): # Print about developers and repository def showDevInfo(): print('\n'+changelog) - print(colorText.BOLD + colorText.WARN + "\n[+] Developer: Pranjal Joshi." + colorText.END) - print(colorText.BOLD + colorText.WARN + ("[+] Version: %s" % VERSION) + colorText.END) - print(colorText.BOLD + colorText.WARN + "[+] More: https://github.com/pranjal-joshi/Screeni-py" + colorText.END) - print(colorText.BOLD + colorText.WARN + "[+] Post Feedback/Issues here: https://github.com/pranjal-joshi/Screeni-py/issues" + colorText.END) - print(colorText.BOLD + colorText.WARN + "[+] Download latest software from https://github.com/pranjal-joshi/Screeni-py/releases/latest" + colorText.END) + print(colorText.BOLD + colorText.WARN + + "\n[+] Developer: Pranjal Joshi." + colorText.END) + print(colorText.BOLD + colorText.WARN + + ("[+] Version: %s" % VERSION) + colorText.END) + print(colorText.BOLD + colorText.WARN + + "[+] More: https://github.com/pranjal-joshi/Screeni-py" + colorText.END) + print(colorText.BOLD + colorText.WARN + + "[+] Post Feedback/Issues here: https://github.com/pranjal-joshi/Screeni-py/issues" + colorText.END) + print(colorText.BOLD + colorText.WARN + + "[+] Download latest software from https://github.com/pranjal-joshi/Screeni-py/releases/latest" + colorText.END) input('') # Save last screened result to pickle file @@ -59,18 +66,23 @@ def setLastScreenedResults(df): df.sort_values(by=['Stock'], ascending=True, inplace=True) df.to_pickle(lastScreened) except IOError: - input(colorText.BOLD + colorText.FAIL + '[+] Failed to save recently screened result table on disk! Skipping..' + colorText.END) + input(colorText.BOLD + colorText.FAIL + + '[+] Failed to save recently screened result table on disk! Skipping..' + colorText.END) # Load last screened result to pickle file def getLastScreenedResults(): try: df = pd.read_pickle(lastScreened) - print(colorText.BOLD + colorText.GREEN + '\n[+] Showing recently screened results..\n' + colorText.END) + print(colorText.BOLD + colorText.GREEN + + '\n[+] Showing recently screened results..\n' + colorText.END) print(tabulate(df, headers='keys', tablefmt='psql')) - print(colorText.BOLD + colorText.WARN + "[+] Note: Trend calculation is based on number of recent days to screen as per your configuration." + colorText.END) - input(colorText.BOLD + colorText.GREEN + '[+] Press any key to continue..' + colorText.END) + print(colorText.BOLD + colorText.WARN + + "[+] Note: Trend calculation is based on number of recent days to screen as per your configuration." + colorText.END) + input(colorText.BOLD + colorText.GREEN + + '[+] Press any key to continue..' + colorText.END) except FileNotFoundError: - print(colorText.BOLD + colorText.FAIL + '[+] Failed to load recently screened result table from disk! Skipping..' + colorText.END) + print(colorText.BOLD + colorText.FAIL + + '[+] Failed to load recently screened result table from disk! Skipping..' + colorText.END) def isTradingTime(): curr = datetime.datetime.now(pytz.timezone('Asia/Kolkata')) @@ -78,55 +90,75 @@ def isTradingTime(): closeTime = curr.replace(hour=15, minute=30) return ((openTime <= curr <= closeTime) and (0 <= curr.weekday() <= 4)) - def saveStockData(stockDict, configManager, loadCount, screenCounter): today_date = datetime.date.today().strftime("%d%m%y") cache_file = "stock_data_" + str(today_date) + ".pkl" + weekday = datetime.date.today().weekday() + if weekday == 5 or weekday == 6: + last_friday = datetime.datetime.today() - datetime.timedelta(days=weekday - 4) + last_friday = last_friday.strftime("%d%m%y") + cache_file = "stock_data_" + str(last_friday) + ".pkl" configManager.deleteStockData(excludeFile=cache_file) if not os.path.exists(cache_file) or screenCounter > (loadCount+1): with open(cache_file, 'wb') as f: try: pickle.dump(stockDict.copy(), f) - print(colorText.BOLD + colorText.GREEN + "=> Done." + colorText.END) + print(colorText.BOLD + colorText.GREEN + + "=> Done." + colorText.END) except pickle.PicklingError: - print(colorText.BOLD + colorText.FAIL + "=> Error while Caching Stock Data." + colorText.END) + print(colorText.BOLD + colorText.FAIL + + "=> Error while Caching Stock Data." + colorText.END) else: - print(colorText.BOLD + colorText.GREEN + "=> Already Cached." + colorText.END) + print(colorText.BOLD + colorText.GREEN + + "=> Already Cached." + colorText.END) def loadStockData(stockDict): today_date = datetime.date.today().strftime("%d%m%y") cache_file = "stock_data_" + str(today_date) + ".pkl" + weekday = datetime.date.today().weekday() + if weekday == 5 or weekday == 6: + last_friday = datetime.datetime.today() - datetime.timedelta(days=weekday - 4) + last_friday = last_friday.strftime("%d%m%y") + cache_file = "stock_data_" + str(last_friday) + ".pkl" if os.path.exists(cache_file): with open(cache_file, 'rb') as f: try: stockData = pickle.load(f) - print(colorText.BOLD + colorText.GREEN + "[+] Automatically Using Cached Stock Data due to After-Market hours!" + colorText.END) + print(colorText.BOLD + colorText.GREEN + + "[+] Automatically Using Cached Stock Data due to After-Market hours!" + colorText.END) for stock in stockData: stockDict[stock] = stockData.get(stock) except pickle.UnpicklingError: - print(colorText.BOLD + colorText.FAIL + "[+] Error while Reading Stock Cache." + colorText.END) + print(colorText.BOLD + colorText.FAIL + + "[+] Error while Reading Stock Cache." + colorText.END) except EOFError: - print(colorText.BOLD + colorText.FAIL + "[+] Stock Cache Corrupted." + colorText.END) + print(colorText.BOLD + colorText.FAIL + + "[+] Stock Cache Corrupted." + colorText.END) # Save screened results to excel + def promptSaveResults(df): try: - response = str(input(colorText.BOLD + colorText.WARN + '[>] Do you want to save the results in excel file? [Y/N]: ')).upper() + response = str(input(colorText.BOLD + colorText.WARN + + '[>] Do you want to save the results in excel file? [Y/N]: ')).upper() except ValueError: response = 'Y' if response != 'N': - filename = 'screenipy-result_'+datetime.datetime.now().strftime("%d-%m-%y_%H.%M.%S")+".xlsx" + filename = 'screenipy-result_' + \ + datetime.datetime.now().strftime("%d-%m-%y_%H.%M.%S")+".xlsx" df.to_excel(filename) - print(colorText.BOLD + colorText.GREEN + ("[+] Results saved to %s" % filename) + colorText.END) + print(colorText.BOLD + colorText.GREEN + + ("[+] Results saved to %s" % filename) + colorText.END) # Prompt for asking RSI def promptRSIValues(): try: - minRSI, maxRSI = int(input(colorText.BOLD + colorText.WARN + "\n[+] Enter Min RSI value: " + colorText.END)), int(input(colorText.BOLD + colorText.WARN + "[+] Enter Max RSI value: " + colorText.END)) + minRSI, maxRSI = int(input(colorText.BOLD + colorText.WARN + "\n[+] Enter Min RSI value: " + colorText.END)), int( + input(colorText.BOLD + colorText.WARN + "[+] Enter Max RSI value: " + colorText.END)) if (minRSI >= 0 and minRSI <= 100) and (maxRSI >= 0 and maxRSI <= 100) and (minRSI <= maxRSI): return (minRSI, maxRSI) raise ValueError except ValueError: - return (0,0) + return (0, 0) # Prompt for Reversal screening def promptReversalScreening(): @@ -152,11 +184,13 @@ def promptChartPatterns(): 0 > Cancel [+] Select option: """ + colorText.END)) if resp == 1 or resp == 2: - candles = int(input(colorText.BOLD + colorText.WARN + "\n[+] How many candles (TimeFrame) to look back Inside Bar formation? : " + colorText.END)) + candles = int(input(colorText.BOLD + colorText.WARN + + "\n[+] How many candles (TimeFrame) to look back Inside Bar formation? : " + colorText.END)) return (resp, candles) if resp >= 0 and resp <= 2: return resp raise ValueError except ValueError: - input(colorText.BOLD + colorText.FAIL + "\n[+] Invalid Option Selected. Press Any Key to Continue..." + colorText.END) - return (None, None) \ No newline at end of file + input(colorText.BOLD + colorText.FAIL + + "\n[+] Invalid Option Selected. Press Any Key to Continue..." + colorText.END) + return (None, None) diff --git a/src/release.md b/src/release.md index 937770f..61c58d5 100644 --- a/src/release.md +++ b/src/release.md @@ -5,12 +5,13 @@ 2. New Improved **Breakout Detection.** :rocket: 3. New **Chart Pattern** **`Bullish Momentum Gainer`** added! Try `Option > 6 > 3` :tada: 4. **Data Saver & High Performance Mode**: Intellegently Stores Stock Data for After-Market hours screening without using extra bandwidth. Also, uses multiple CPU cores available on your computer for supperfast screening! :sparkles: (Thanks to [**swarpatel23**](https://github.com/swarpatel23)) -5. Cosmetic Updates! :lipstick: +5. Cosmetic Updates - Progressbar for showing screening process! :lipstick: +6. Performance Upgrades! :gear: ## Downloads -* For :desktop_computer: **Windows** users, download **[screenipy.exe](https://github.com/pranjal-joshi/Screeni-py/releases/download/1.17/screenipy.exe)** -* For :penguin: **Linux** users, download **[screenipy.bin](https://github.com/pranjal-joshi/Screeni-py/releases/download/1.17/screenipy.bin)** -* For :apple: **MacOS** users, download **[screenipy.run](https://github.com/pranjal-joshi/Screeni-py/releases/download/1.17/screenipy.run)** ([Read Installation Guide](https://github.com/pranjal-joshi/Screeni-py/blob/main/INSTALLATION.md#for-macos)) +* For :desktop_computer: **Windows** users, download **[screenipy.exe](https://github.com/pranjal-joshi/Screeni-py/releases/download/1.18/screenipy.exe)** +* For :penguin: **Linux** users, download **[screenipy.bin](https://github.com/pranjal-joshi/Screeni-py/releases/download/1.18/screenipy.bin)** +* For :apple: **MacOS** users, download **[screenipy.run](https://github.com/pranjal-joshi/Screeni-py/releases/download/1.18/screenipy.run)** ([Read Installation Guide](https://github.com/pranjal-joshi/Screeni-py/blob/main/INSTALLATION.md#for-macos)) ## How to use? diff --git a/src/screenipy.py b/src/screenipy.py index 68847e5..cb838ae 100644 --- a/src/screenipy.py +++ b/src/screenipy.py @@ -269,14 +269,14 @@ def main(testing=False): screenResults.rename( columns={ 'Trend': f'Trend ({configManager.daysToLookback}Days)', - 'Breaking-Out': 'Breakout-Levels' + 'Breaking-Out': f'Breakout ({configManager.daysToLookback}Days)' }, inplace=True ) saveResults.rename( columns={ 'Trend': f'Trend ({configManager.daysToLookback}Days)', - 'Breaking-Out': 'Breakout-Levels' + 'Breaking-Out': 'Breakout ({configManager.daysToLookback}Days)' }, inplace=True )