pkscreener 0.46.20250224.736__tar.gz → 0.46.20250224.737__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/PKG-INFO +8 -8
  2. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/README.md +5 -5
  3. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/README.txt +5 -5
  4. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/AssetsManager.py +58 -90
  5. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/ConsoleUtility.py +3 -0
  6. pkscreener-0.46.20250224.737/pkscreener/classes/__init__.py +1 -0
  7. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/globals.py +31 -16
  8. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/pkscreenercli.py +5 -0
  9. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/requirements.txt +1 -1
  10. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener.egg-info/PKG-INFO +8 -8
  11. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener.egg-info/requires.txt +1 -1
  12. pkscreener-0.46.20250224.736/pkscreener/classes/__init__.py +0 -1
  13. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/LICENSE +0 -0
  14. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/Disclaimer.txt +0 -0
  15. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/LICENSE-Others.txt +0 -0
  16. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/LICENSE.txt +0 -0
  17. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/LogoWM.txt +0 -0
  18. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/__init__.py +0 -0
  19. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/ArtTexts.py +0 -0
  20. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/Backtest.py +0 -0
  21. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/Barometer.py +0 -0
  22. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/BaseScreeningStatistics.py +0 -0
  23. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/CandlePatterns.py +0 -0
  24. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/Changelog.py +0 -0
  25. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/ConfigManager.py +0 -0
  26. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/ConsoleMenuUtility.py +0 -0
  27. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/Fetcher.py +0 -0
  28. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/GlobalStore.py +0 -0
  29. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/ImageUtility.py +0 -0
  30. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/MarketMonitor.py +0 -0
  31. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/MarketStatus.py +0 -0
  32. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/MenuOptions.py +0 -0
  33. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/Messenger.py +0 -0
  34. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/OtaUpdater.py +0 -0
  35. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/PKAnalytics.py +0 -0
  36. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/PKDataService.py +0 -0
  37. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/PKDemoHandler.py +0 -0
  38. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/PKMarketOpenCloseAnalyser.py +0 -0
  39. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/PKPremiumHandler.py +0 -0
  40. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/PKScanRunner.py +0 -0
  41. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/PKScheduledTaskProgress.py +0 -0
  42. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/PKScheduler.py +0 -0
  43. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/PKSpreadsheets.py +0 -0
  44. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/PKTask.py +0 -0
  45. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/PKUserRegistration.py +0 -0
  46. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/Pktalib.py +0 -0
  47. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/Portfolio.py +0 -0
  48. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/PortfolioXRay.py +0 -0
  49. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/ScreeningStatistics.py +0 -0
  50. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/StockScreener.py +0 -0
  51. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/StockSentiment.py +0 -0
  52. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/UserMenuChoicesHandler.py +0 -0
  53. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/Utility.py +0 -0
  54. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/WorkflowManager.py +0 -0
  55. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/classes/keys.py +0 -0
  56. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/courbd.ttf +0 -0
  57. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/pkscreener.ini +0 -0
  58. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener/pkscreenerbot.py +0 -0
  59. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener.egg-info/SOURCES.txt +0 -0
  60. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener.egg-info/dependency_links.txt +0 -0
  61. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener.egg-info/entry_points.txt +0 -0
  62. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener.egg-info/not-zip-safe +0 -0
  63. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/pkscreener.egg-info/top_level.txt +0 -0
  64. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/setup.cfg +0 -0
  65. {pkscreener-0.46.20250224.736 → pkscreener-0.46.20250224.737}/setup.py +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pkscreener
3
- Version: 0.46.20250224.736
3
+ Version: 0.46.20250224.737
4
4
  Summary: A Python-based stock screener for NSE, India with alerts to Telegram Channel (pkscreener)
5
5
  Home-page: https://github.com/pkjmesra/pkscreener
6
- Download-URL: https://github.com/pkjmesra/pkscreener/archive/v0.46.20250224.736.zip
6
+ Download-URL: https://github.com/pkjmesra/pkscreener/archive/v0.46.20250224.737.zip
7
7
  Author: pkjmesra
8
8
  Author-email: pkjmesra@gmail.com
9
9
  License: OSI Approved (MIT)
@@ -33,7 +33,7 @@ Requires-Dist: pandas
33
33
  Requires-Dist: pandas_ta
34
34
  Requires-Dist: pefile<2024.8.26,>=2023.2.7
35
35
  Requires-Dist: Pillow
36
- Requires-Dist: PKDevTools>=0.13.20250126.241
36
+ Requires-Dist: PKDevTools>=0.13.20250126.243
37
37
  Requires-Dist: PKNSETools>=0.1.20250122.139
38
38
  Requires-Dist: Pyarrow
39
39
  Requires-Dist: pyppeteer
@@ -392,15 +392,15 @@ After you have finished the run, go to that copied path, zip the contents of the
392
392
  [MADE-IN-INDIA-badge]: https://img.shields.io/badge/MADE%20WITH%20%E2%9D%A4%20IN-INDIA-orange
393
393
  [MADE-IN-INDIA]: https://en.wikipedia.org/wiki/India
394
394
  [Windows-badge]: https://img.shields.io/badge/Windows-0078D6?logo=windows&logoColor=white
395
- [Windows]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250222.735/pkscreenercli.exe
395
+ [Windows]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250224.736/pkscreenercli.exe
396
396
  [Linux-badge_x64]: https://img.shields.io/badge/Linux(x64)-FCC624?logo=linux&logoColor=black
397
- [Linux_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250222.735/pkscreenercli_x64.bin
397
+ [Linux_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250224.736/pkscreenercli_x64.bin
398
398
  [Linux-badge_arm64]: https://img.shields.io/badge/Linux(arm64)-FCC624?logo=linux&logoColor=black
399
- [Linux_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250222.735/pkscreenercli_arm64.bin
399
+ [Linux_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250224.736/pkscreenercli_arm64.bin
400
400
  [Mac OS-badge_x64]: https://img.shields.io/badge/mac%20os(x64)-D3D3D3?logo=apple&logoColor=000000
401
- [Mac OS_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250222.735/pkscreenercli_x64.run
401
+ [Mac OS_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250224.736/pkscreenercli_x64.run
402
402
  [Mac OS-badge_arm64]: https://img.shields.io/badge/mac%20os(arm64)-D3D3D3?logo=apple&logoColor=000000
403
- [Mac OS_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250222.735/pkscreenercli_arm64.run
403
+ [Mac OS_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250224.736/pkscreenercli_arm64.run
404
404
  [GitHub release (latest by date)-badge]: https://img.shields.io/github/v/release/pkjmesra/PKScreener
405
405
  [GitHub release (latest by date)]: https://github.com/pkjmesra/PKScreener/releases/latest
406
406
  [pypi-badge]: https://img.shields.io/pypi/v/pkscreener.svg?style=flat-square
@@ -328,15 +328,15 @@ After you have finished the run, go to that copied path, zip the contents of the
328
328
  [MADE-IN-INDIA-badge]: https://img.shields.io/badge/MADE%20WITH%20%E2%9D%A4%20IN-INDIA-orange
329
329
  [MADE-IN-INDIA]: https://en.wikipedia.org/wiki/India
330
330
  [Windows-badge]: https://img.shields.io/badge/Windows-0078D6?logo=windows&logoColor=white
331
- [Windows]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250222.735/pkscreenercli.exe
331
+ [Windows]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250224.736/pkscreenercli.exe
332
332
  [Linux-badge_x64]: https://img.shields.io/badge/Linux(x64)-FCC624?logo=linux&logoColor=black
333
- [Linux_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250222.735/pkscreenercli_x64.bin
333
+ [Linux_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250224.736/pkscreenercli_x64.bin
334
334
  [Linux-badge_arm64]: https://img.shields.io/badge/Linux(arm64)-FCC624?logo=linux&logoColor=black
335
- [Linux_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250222.735/pkscreenercli_arm64.bin
335
+ [Linux_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250224.736/pkscreenercli_arm64.bin
336
336
  [Mac OS-badge_x64]: https://img.shields.io/badge/mac%20os(x64)-D3D3D3?logo=apple&logoColor=000000
337
- [Mac OS_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250222.735/pkscreenercli_x64.run
337
+ [Mac OS_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250224.736/pkscreenercli_x64.run
338
338
  [Mac OS-badge_arm64]: https://img.shields.io/badge/mac%20os(arm64)-D3D3D3?logo=apple&logoColor=000000
339
- [Mac OS_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250222.735/pkscreenercli_arm64.run
339
+ [Mac OS_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250224.736/pkscreenercli_arm64.run
340
340
  [GitHub release (latest by date)-badge]: https://img.shields.io/github/v/release/pkjmesra/PKScreener
341
341
  [GitHub release (latest by date)]: https://github.com/pkjmesra/PKScreener/releases/latest
342
342
  [pypi-badge]: https://img.shields.io/pypi/v/pkscreener.svg?style=flat-square
@@ -328,15 +328,15 @@ After you have finished the run, go to that copied path, zip the contents of the
328
328
  [MADE-IN-INDIA-badge]: https://img.shields.io/badge/MADE%20WITH%20%E2%9D%A4%20IN-INDIA-orange
329
329
  [MADE-IN-INDIA]: https://en.wikipedia.org/wiki/India
330
330
  [Windows-badge]: https://img.shields.io/badge/Windows-0078D6?logo=windows&logoColor=white
331
- [Windows]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250222.735/pkscreenercli.exe
331
+ [Windows]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250224.736/pkscreenercli.exe
332
332
  [Linux-badge_x64]: https://img.shields.io/badge/Linux(x64)-FCC624?logo=linux&logoColor=black
333
- [Linux_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250222.735/pkscreenercli_x64.bin
333
+ [Linux_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250224.736/pkscreenercli_x64.bin
334
334
  [Linux-badge_arm64]: https://img.shields.io/badge/Linux(arm64)-FCC624?logo=linux&logoColor=black
335
- [Linux_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250222.735/pkscreenercli_arm64.bin
335
+ [Linux_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250224.736/pkscreenercli_arm64.bin
336
336
  [Mac OS-badge_x64]: https://img.shields.io/badge/mac%20os(x64)-D3D3D3?logo=apple&logoColor=000000
337
- [Mac OS_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250222.735/pkscreenercli_x64.run
337
+ [Mac OS_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250224.736/pkscreenercli_x64.run
338
338
  [Mac OS-badge_arm64]: https://img.shields.io/badge/mac%20os(arm64)-D3D3D3?logo=apple&logoColor=000000
339
- [Mac OS_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250222.735/pkscreenercli_arm64.run
339
+ [Mac OS_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250224.736/pkscreenercli_arm64.run
340
340
  [GitHub release (latest by date)-badge]: https://img.shields.io/github/v/release/pkjmesra/PKScreener
341
341
  [GitHub release (latest by date)]: https://github.com/pkjmesra/PKScreener/releases/latest
342
342
  [pypi-badge]: https://img.shields.io/pypi/v/pkscreener.svg?style=flat-square
@@ -231,11 +231,11 @@ class PKAssetsManager:
231
231
  pickle.dump(stockDict.copy(), f, protocol=pickle.HIGHEST_PROTOCOL)
232
232
  OutputControls().printOutput(colorText.GREEN + "=> Done." + colorText.END)
233
233
  if downloadOnly:
234
- if "RUNNER" not in os.environ.keys():
235
- copyFilePath = os.path.join(Archiver.get_user_data_dir(), f"copy_{fileName}")
236
- cacheFileSize = os.stat(cache_file).st_size if os.path.exists(cache_file) else 0
237
- if os.path.exists(cache_file) and cacheFileSize >= 1024*1024*40:
238
- shutil.copy(cache_file,copyFilePath) # copy is the saved source of truth
234
+ # if "RUNNER" not in os.environ.keys():
235
+ # copyFilePath = os.path.join(Archiver.get_user_data_dir(), f"copy_{fileName}")
236
+ # cacheFileSize = os.stat(cache_file).st_size if os.path.exists(cache_file) else 0
237
+ # if os.path.exists(cache_file) and cacheFileSize >= 1024*1024*40:
238
+ # shutil.copy(cache_file,copyFilePath) # copy is the saved source of truth
239
239
 
240
240
  rootDirs = [Archiver.get_user_data_dir(),Archiver.get_user_indices_dir(),outputFolder]
241
241
  patterns = ["*.csv","*.pkl"]
@@ -268,7 +268,7 @@ class PKAssetsManager:
268
268
  def had_rate_limit_errors():
269
269
  """Checks if any stored errors are YFRateLimitError."""
270
270
  err = ",".join(list(shared._ERRORS.values()))
271
- hitRateLimit = "YFRateLimitError" in err or "Too Many Requests" in err
271
+ hitRateLimit = "YFRateLimitError" in err or "Too Many Requests" in err or "429" in err
272
272
  if hitRateLimit:
273
273
  OutputControls().printOutput(
274
274
  colorText.FAIL
@@ -363,10 +363,10 @@ class PKAssetsManager:
363
363
  f"Stock data cache file:{cache_file} exists ->{str(exists)}"
364
364
  )
365
365
  stockDataLoaded = False
366
- copyFilePath = os.path.join(Archiver.get_user_data_dir(), f"copy_{cache_file}")
366
+ # copyFilePath = os.path.join(Archiver.get_user_data_dir(), f"copy_{cache_file}")
367
367
  srcFilePath = os.path.join(Archiver.get_user_data_dir(), cache_file)
368
- if os.path.exists(copyFilePath):
369
- shutil.copy(copyFilePath,srcFilePath) # copy is the saved source of truth
368
+ # if os.path.exists(copyFilePath):
369
+ # shutil.copy(copyFilePath,srcFilePath) # copy is the saved source of truth
370
370
  if os.path.exists(srcFilePath) and not forceRedownload:
371
371
  stockDict, stockDataLoaded = PKAssetsManager.loadDataFromLocalPickle(stockDict,configManager, downloadOnly, defaultAnswer, exchangeSuffix, cache_file, isTrading)
372
372
  if (
@@ -387,93 +387,61 @@ class PKAssetsManager:
387
387
  stockDict, _ = PKAssetsManager.downloadLatestData(stockDict,configManager,stockCodes,exchangeSuffix=exchangeSuffix,downloadOnly=downloadOnly,numStocksPerIteration=len(stockCodes) if stockCodes is not None else 0)
388
388
  # See if we need to save stock data
389
389
  stockDataLoaded = stockDataLoaded or (len(stockDict) > 0 and (len(stockDict) != initialLoadCount))
390
- if stockDataLoaded:
391
- PKAssetsManager.saveStockData(stockDict,configManager,initialLoadCount,isIntraday,downloadOnly, forceSave=stockDataLoaded)
392
390
  leftOutStocks = list(set(stockCodes)-set(list(stockDict.keys())))
393
391
  if len(leftOutStocks) > int(len(stockCodes)*0.05) and not PKAssetsManager.had_rate_limit_errors():
394
392
  # More than 5 % of stocks are still remaining
395
393
  stockDict, _ = PKAssetsManager.downloadLatestData(stockDict,configManager,leftOutStocks,exchangeSuffix=exchangeSuffix,downloadOnly=downloadOnly,numStocksPerIteration=len(leftOutStocks) if leftOutStocks is not None else 0)
394
+ if stockDataLoaded and downloadOnly:
395
+ PKAssetsManager.saveStockData(stockDict,configManager,initialLoadCount,isIntraday,downloadOnly, forceSave=stockDataLoaded)
396
396
  return stockDict
397
397
 
398
398
  @Halo(text=' [+] Loading data from local cache...', spinner='dots')
399
399
  def loadDataFromLocalPickle(stockDict, configManager, downloadOnly, defaultAnswer, exchangeSuffix, cache_file, isTrading):
400
400
  stockDataLoaded = False
401
401
  srcFilePath = os.path.join(Archiver.get_user_data_dir(), cache_file)
402
- with open(srcFilePath, "rb") as f:
403
- try:
402
+
403
+ try:
404
+ with open(srcFilePath, "rb") as f:
404
405
  stockData = pickle.load(f)
405
- if not downloadOnly:
406
- OutputControls().printOutput(
407
- colorText.GREEN
408
- + f"\n [+] Automatically Using [{len(stockData)}] Tickers' Cached Stock Data {'due to After-Market hours' if not PKDateUtilities.isTradingTime() else ''}!"
409
- + colorText.END
410
- )
411
- if stockData is not None and len(stockData) > 0:
412
- multiIndex = stockData.keys()
413
- if isinstance(multiIndex, pd.MultiIndex):
414
- # If we requested for multiple stocks from yfinance
415
- # we'd have received a multiindex dataframe
416
- listStockCodes = multiIndex.get_level_values(0)
417
- listStockCodes = sorted(list(filter(None,list(set(listStockCodes)))))
418
- if len(listStockCodes) > 0 and len(exchangeSuffix) > 0 and exchangeSuffix in listStockCodes[0]:
419
- listStockCodes = [x.replace(exchangeSuffix,"") for x in listStockCodes]
420
- else:
421
- listStockCodes = list(stockData.keys())
422
- if len(listStockCodes) > 0 and len(exchangeSuffix) > 0 and exchangeSuffix in listStockCodes[0]:
423
- listStockCodes = [x.replace(exchangeSuffix,"") for x in listStockCodes]
424
- for stock in listStockCodes:
425
- df_or_dict = stockData.get(stock)
426
- df_or_dict = df_or_dict.to_dict("split") if isinstance(df_or_dict,pd.DataFrame) else df_or_dict
427
- # This will keep all the latest security data we downloaded
428
- # just now and also copy the additional data like, MF/FII,FairValue
429
- # etc. data, from yesterday's saved data.
430
- try:
431
- existingPreLoadedData = stockDict.get(stock)
432
- if existingPreLoadedData is not None:
433
- if isTrading:
434
- # Only copy the MF/FII/FairValue data and leave the stock prices as is.
435
- cols = ["MF", "FII","MF_Date","FII_Date","FairValue"]
436
- for col in cols:
437
- existingPreLoadedData[col] = df_or_dict.get(col)
438
- stockDict[stock] = existingPreLoadedData
439
- else:
440
- stockDict[stock] = df_or_dict | existingPreLoadedData
441
- else:
442
- if not isTrading:
443
- stockDict[stock] = df_or_dict
444
- except KeyboardInterrupt: # pragma: no cover
445
- raise KeyboardInterrupt
446
- except: # pragma: no cover
447
- # Probably, the "stock" got removed from the latest download
448
- # and so, was not found in stockDict
449
- continue
450
- # if len(stockDict) > 0:
451
- # stockDict = stockDict | stockData
452
- # else:
453
- # stockDict = stockData
454
- stockDataLoaded = True
455
- except KeyboardInterrupt: # pragma: no cover
456
- raise KeyboardInterrupt
457
- except pickle.UnpicklingError as e: # pragma: no cover
458
- default_logger().debug(e, exc_info=True)
459
- f.close()
406
+ if not stockData:
407
+ return stockDict, stockDataLoaded
408
+ if not downloadOnly:
460
409
  OutputControls().printOutput(
461
- colorText.FAIL
462
- + " [+] Error while Reading Stock Cache. Press <Enter> to continue..."
463
- + colorText.END
464
- )
465
- if PKAssetsManager.promptFileExists(defaultAnswer=defaultAnswer) == "Y":
466
- configManager.deleteFileWithPattern()
467
- except EOFError as e: # pragma: no cover
468
- default_logger().debug(e, exc_info=True)
469
- f.close()
470
- OutputControls().printOutput(
471
- colorText.FAIL
472
- + " [+] Stock Cache Corrupted."
473
- + colorText.END
474
- )
475
- if PKAssetsManager.promptFileExists(defaultAnswer=defaultAnswer) == "Y":
476
- configManager.deleteFileWithPattern()
410
+ colorText.GREEN
411
+ + f"\n [+] Automatically Using [{len(stockData)}] Tickers' Cached Stock Data"
412
+ + (" due to After-Market hours" if not PKDateUtilities.isTradingTime() else "")
413
+ + colorText.END
414
+ )
415
+ multiIndex = stockData.keys()
416
+ if isinstance(multiIndex, pd.MultiIndex):
417
+ listStockCodes = sorted(set(multiIndex.get_level_values(0)))
418
+ else:
419
+ listStockCodes = list(stockData.keys())
420
+ if exchangeSuffix and any(exchangeSuffix in code for code in listStockCodes):
421
+ listStockCodes = [x.replace(exchangeSuffix, "") for x in listStockCodes]
422
+ for stock in listStockCodes:
423
+ df_or_dict = stockData.get(stock)
424
+ df_or_dict = df_or_dict.to_dict("split") if isinstance(df_or_dict, pd.DataFrame) else df_or_dict
425
+ existingPreLoadedData = stockDict.get(stock)
426
+ if existingPreLoadedData:
427
+ if isTrading:
428
+ for col in ["MF", "FII", "MF_Date", "FII_Date", "FairValue"]:
429
+ existingPreLoadedData[col] = df_or_dict.get(col)
430
+ stockDict[stock] = existingPreLoadedData
431
+ else:
432
+ stockDict[stock] = {**existingPreLoadedData, **df_or_dict}
433
+ elif not isTrading:
434
+ stockDict[stock] = df_or_dict
435
+ stockDataLoaded = True
436
+ except (pickle.UnpicklingError, EOFError) as e:
437
+ default_logger().debug(e, exc_info=True)
438
+ OutputControls().printOutput(
439
+ colorText.FAIL + " [+] Error while Reading Stock Cache." + colorText.END
440
+ )
441
+ if PKAssetsManager.promptFileExists(defaultAnswer=defaultAnswer) == "Y":
442
+ configManager.deleteFileWithPattern()
443
+ except KeyboardInterrupt:
444
+ raise
477
445
  return stockDict, stockDataLoaded
478
446
 
479
447
  @Halo(text='', spinner='dots')
@@ -575,12 +543,12 @@ class PKAssetsManager:
575
543
  # and so, was not found in stockDict
576
544
  continue
577
545
  stockDataLoaded = True
578
- copyFilePath = os.path.join(Archiver.get_user_data_dir(), f"copy_{cache_file}")
579
- srcFilePath = os.path.join(Archiver.get_user_data_dir(), cache_file)
580
- if os.path.exists(copyFilePath) and os.path.exists(srcFilePath):
581
- shutil.copy(copyFilePath,srcFilePath) # copy is the saved source of truth
582
- if not os.path.exists(copyFilePath) and os.path.exists(srcFilePath): # Let's make a copy of the original one
583
- shutil.copy(srcFilePath,copyFilePath)
546
+ # copyFilePath = os.path.join(Archiver.get_user_data_dir(), f"copy_{cache_file}")
547
+ # srcFilePath = os.path.join(Archiver.get_user_data_dir(), cache_file)
548
+ # if os.path.exists(copyFilePath) and os.path.exists(srcFilePath):
549
+ # shutil.copy(copyFilePath,srcFilePath) # copy is the saved source of truth
550
+ # if not os.path.exists(copyFilePath) and os.path.exists(srcFilePath): # Let's make a copy of the original one
551
+ # shutil.copy(srcFilePath,copyFilePath)
584
552
  # Remove the progress bar now!
585
553
  OutputControls().moveCursorUpLines(1)
586
554
  except KeyboardInterrupt: # pragma: no cover
@@ -41,6 +41,9 @@ class PKConsoleTools:
41
41
 
42
42
  fetcher = Fetcher.screenerStockDataFetcher()
43
43
  def clearScreen(userArgs=None,clearAlways=False,forceTop=False):
44
+ timeit = 'timeit' in os.environ.keys()
45
+ if timeit:
46
+ return
44
47
  if "RUNNER" in os.environ.keys() or (userArgs is not None and userArgs.prodbuild):
45
48
  if userArgs is not None and userArgs.v:
46
49
  os.environ["RUNNER"]="LOCAL_RUN_SCANNER"
@@ -0,0 +1 @@
1
+ VERSION='0.46.20250224.737'
@@ -75,6 +75,7 @@ from PKDevTools.classes.OutputControls import OutputControls
75
75
  from PKDevTools.classes.Environment import PKEnvironment
76
76
  from pkscreener.classes.CandlePatterns import CandlePatterns
77
77
  from pkscreener.classes import AssetsManager
78
+ from PKDevTools.classes.FunctionTimeouts import exit_after
78
79
  from pkscreener.classes.MenuOptions import (
79
80
  level0MenuDict,
80
81
  level1_X_MenuDict,
@@ -498,7 +499,7 @@ def handleSecondaryMenuChoices(
498
499
  selectedMenu = m0.find(options[0])
499
500
  m1.renderForMenu(selectedMenu=selectedMenu, asList=True)
500
501
  selectedMenu = m1.find(options[1])
501
- m2.renderForMenu(selectedMenu=selectedMenu)
502
+ m2.renderForMenu(selectedMenu=selectedMenu,asList=True)
502
503
  if options[2] in ["1","2","3","4"]:
503
504
  selectedMenu = m2.find(options[2])
504
505
  periodDurations = selectedMenu.menuText.split("(")[1].split(")")[0].split(", ")
@@ -552,7 +553,7 @@ def initExecution(menuOption=None):
552
553
  + f" (Piped Scan Mode) [{userPassedArgs.pipedmenus}]"
553
554
  + colorText.END
554
555
  )
555
- m0.renderForMenu(selectedMenu=None)
556
+ m0.renderForMenu(selectedMenu=None,asList=(userPassedArgs is not None and userPassedArgs.options is not None))
556
557
  try:
557
558
  needsCalc = userPassedArgs is not None and userPassedArgs.backtestdaysago is not None
558
559
  pastDate = f" [+] [ Running in Quick Backtest Mode for {colorText.WARN}{PKDateUtilities.nthPastTradingDateStringFromFutureDate(int(userPassedArgs.backtestdaysago) if needsCalc else 0)}{colorText.END} ]\n" if needsCalc else ""
@@ -612,7 +613,7 @@ def initPostLevel0Execution(
612
613
  )
613
614
  if indexOption is None:
614
615
  selectedMenu = m0.find(menuOption)
615
- m1.renderForMenu(selectedMenu=selectedMenu, skip=skip)
616
+ m1.renderForMenu(selectedMenu=selectedMenu, skip=skip,asList=(userPassedArgs is not None and userPassedArgs.options is not None))
616
617
  try:
617
618
  needsCalc = userPassedArgs is not None and userPassedArgs.backtestdaysago is not None
618
619
  pastDate = f" [+] [ Running in Quick Backtest Mode for {colorText.WARN}{PKDateUtilities.nthPastTradingDateStringFromFutureDate(int(userPassedArgs.backtestdaysago) if needsCalc else 0)}{colorText.END} ]\n" if needsCalc else ""
@@ -679,7 +680,7 @@ def initPostLevel1Execution(indexOption, executeOption=None, skip=[], retrial=Fa
679
680
  + colorText.END
680
681
  )
681
682
  selectedMenu = m1.find(indexOption)
682
- m2.renderForMenu(selectedMenu=selectedMenu, skip=skip)
683
+ m2.renderForMenu(selectedMenu=selectedMenu, skip=skip,asList=(userPassedArgs is not None and userPassedArgs.options is not None))
683
684
  stockIndexCode = str(len(level1_index_options_sectoral.keys()))
684
685
  if indexOption == "S":
685
686
  ensureMenusLoaded("X",indexOption,executeOption)
@@ -701,7 +702,7 @@ def initPostLevel1Execution(indexOption, executeOption=None, skip=[], retrial=Fa
701
702
  listStockCodes = [level1_index_options_sectoral[str(stockIndexCode)].split("(")[1].split(")")[0]]
702
703
  selectedMenu.menuKey = "0" # Reset because user must have selected specific index menu with single stock
703
704
  ConsoleUtility.PKConsoleTools.clearScreen(forceTop=True)
704
- m2.renderForMenu(selectedMenu=selectedMenu, skip=skip)
705
+ m2.renderForMenu(selectedMenu=selectedMenu, skip=skip,asList=(userPassedArgs is not None and userPassedArgs.options is not None))
705
706
  try:
706
707
  needsCalc = userPassedArgs is not None and userPassedArgs.backtestdaysago is not None
707
708
  pastDate = f" [+] [ Running in Quick Backtest Mode for {colorText.WARN}{PKDateUtilities.nthPastTradingDateStringFromFutureDate(int(userPassedArgs.backtestdaysago) if needsCalc else 0)}{colorText.END} ]\n" if needsCalc else ""
@@ -927,7 +928,7 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
927
928
  startMarketMonitor(mkt_monitor_dict,keyboardInterruptEvent)
928
929
 
929
930
  keyboardInterruptEventFired = False
930
- if stockDictPrimary is None:
931
+ if stockDictPrimary is None or isinstance(stockDictPrimary,dict):
931
932
  stockDictPrimary = mp_manager.dict()
932
933
  stockDictSecondary = mp_manager.dict()
933
934
  loadCount = 0
@@ -1098,7 +1099,7 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
1098
1099
  selectedChoice["0"] = "P"
1099
1100
  updateMenuChoiceHierarchy()
1100
1101
  selectedMenu = m0.find(menuOption)
1101
- m1.renderForMenu(selectedMenu)
1102
+ m1.renderForMenu(selectedMenu,asList=(userPassedArgs is not None and userPassedArgs.options is not None))
1102
1103
  needsCalc = userPassedArgs is not None and userPassedArgs.backtestdaysago is not None
1103
1104
  pastDate = f" [+] [ Running in Quick Backtest Mode for {colorText.WARN}{PKDateUtilities.nthPastTradingDateStringFromFutureDate(int(userPassedArgs.backtestdaysago) if needsCalc else 0)}{colorText.END} ]\n" if needsCalc else ""
1104
1105
  if predefinedOption is None:
@@ -1111,7 +1112,7 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
1111
1112
 
1112
1113
  if predefinedOption in ["1", "4"]:
1113
1114
  selectedMenu = m1.find(predefinedOption)
1114
- m2.renderForMenu(selectedMenu=selectedMenu)
1115
+ m2.renderForMenu(selectedMenu=selectedMenu,asList=(userPassedArgs is not None and userPassedArgs.options is not None))
1115
1116
  if selPredefinedOption is None:
1116
1117
  selPredefinedOption = input(colorText.FAIL + f"{pastDate} [+] Select option: ") or "1"
1117
1118
  OutputControls().printOutput(colorText.END, end="")
@@ -1123,7 +1124,7 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
1123
1124
 
1124
1125
  elif predefinedOption == "1": # Predefined
1125
1126
  if selIndexOption is None and (userPassedArgs is None or userPassedArgs.answerdefault is None):
1126
- m1.renderForMenu(m0.find(key="X"),skip=["W","N","E","S","Z"])
1127
+ m1.renderForMenu(m0.find(key="X"),skip=["W","N","E","S","Z"],asList=(userPassedArgs is not None and userPassedArgs.options is not None))
1127
1128
  selIndexOption = input(colorText.FAIL + f"{pastDate} [+] Select option: ") or str(configManager.defaultIndex)
1128
1129
  if str(selIndexOption).upper() in "M":
1129
1130
  return None, None
@@ -1697,7 +1698,7 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
1697
1698
  if executeOption == 40:
1698
1699
  ConsoleUtility.PKConsoleTools.clearScreen(forceTop=True)
1699
1700
  selectedMenu = m2.find(str(executeOption))
1700
- m3.renderForMenu(selectedMenu=selectedMenu)
1701
+ m3.renderForMenu(selectedMenu=selectedMenu,asList=(userPassedArgs is not None and userPassedArgs.options is not None))
1701
1702
  if userPassedArgs.options is not None:
1702
1703
  options = userPassedArgs.options.split(":")
1703
1704
  if len(options) >=4:
@@ -1711,7 +1712,7 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
1711
1712
  respChartPattern = (smaEMA == "2")
1712
1713
  selectedMenu = m3.find(str(smaEMA))
1713
1714
  ConsoleUtility.PKConsoleTools.clearScreen(forceTop=True)
1714
- m4.renderForMenu(selectedMenu=selectedMenu)
1715
+ m4.renderForMenu(selectedMenu=selectedMenu,asList=(userPassedArgs is not None and userPassedArgs.options is not None))
1715
1716
  if len(options) >=5:
1716
1717
  smaDirection = options[4]
1717
1718
  smaDirection = "2" if smaDirection == "D" else smaDirection
@@ -1732,7 +1733,7 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
1732
1733
  if executeOption == 41:
1733
1734
  ConsoleUtility.PKConsoleTools.clearScreen(forceTop=True)
1734
1735
  selectedMenu = m2.find(str(executeOption))
1735
- m3.renderForMenu(selectedMenu=selectedMenu)
1736
+ m3.renderForMenu(selectedMenu=selectedMenu,asList=(userPassedArgs is not None and userPassedArgs.options is not None))
1736
1737
  if userPassedArgs.options is not None:
1737
1738
  options = userPassedArgs.options.split(":")
1738
1739
  if len(options) >=4:
@@ -1746,7 +1747,7 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
1746
1747
  respChartPattern = pivotPoint
1747
1748
  selectedMenu = m3.find(str(pivotPoint))
1748
1749
  ConsoleUtility.PKConsoleTools.clearScreen(forceTop=True)
1749
- m4.renderForMenu(selectedMenu=selectedMenu)
1750
+ m4.renderForMenu(selectedMenu=selectedMenu,asList=(userPassedArgs is not None and userPassedArgs.options is not None))
1750
1751
  if len(options) >=5:
1751
1752
  priceDirection = options[4]
1752
1753
  priceDirection = "2" if priceDirection == "D" else priceDirection
@@ -2365,6 +2366,20 @@ def analysisFinalResults(screenResults,saveResults,optionalFinalOutcome_df,runOp
2365
2366
  analysis_dict[firstScanKey] = {"S1": screenResults, "S2": saveResults}
2366
2367
  return optionalFinalOutcome_df, saveResults
2367
2368
 
2369
+ @exit_after(10)
2370
+ def tryLoadDataOnBackgroundThread():
2371
+ global stockDictPrimary,stockDictSecondary, configManager, defaultAnswer, userPassedArgs, loadedStockData
2372
+ if stockDictPrimary is None:
2373
+ stockDictPrimary = {}
2374
+ stockDictSecondary = {}
2375
+ loadedStockData = False
2376
+ configManager.getConfig(parser=ConfigManager.parser)
2377
+ defaultAnswer = "Y"
2378
+ userPassedArgs = None
2379
+ with SuppressOutput(suppress_stderr=True, suppress_stdout=True):
2380
+ listStockCodes = fetcher.fetchStockCodes(int(configManager.defaultIndex), stockCode=None)
2381
+ loadDatabaseOrFetch(downloadOnly=True,listStockCodes=listStockCodes,menuOption="X",indexOption=int(configManager.defaultIndex))
2382
+
2368
2383
  def loadDatabaseOrFetch(downloadOnly, listStockCodes, menuOption, indexOption):
2369
2384
  global stockDictPrimary,stockDictSecondary, configManager, defaultAnswer, userPassedArgs, loadedStockData
2370
2385
  if menuOption not in ["C"]:
@@ -2378,16 +2393,16 @@ def loadDatabaseOrFetch(downloadOnly, listStockCodes, menuOption, indexOption):
2378
2393
  exchangeSuffix = "" if (indexOption == 15 or (configManager.defaultIndex == 15 and indexOption == 0)) else ".NS",
2379
2394
  userDownloadOption = menuOption
2380
2395
  )
2381
- if menuOption not in ["C"] and (userPassedArgs.monitor is not None or \
2396
+ if menuOption not in ["C"] and (userPassedArgs is not None and (userPassedArgs.monitor is not None or \
2382
2397
  ("|" in userPassedArgs.options and ':i' in userPassedArgs.options) or \
2383
2398
  (":33:3:" in userPassedArgs.options or \
2384
2399
  ":32:" in userPassedArgs.options or \
2385
- ":38:" in userPassedArgs.options)) :#not configManager.isIntradayConfig() and configManager.calculatersiintraday:
2400
+ ":38:" in userPassedArgs.options))) :#not configManager.isIntradayConfig() and configManager.calculatersiintraday:
2386
2401
  prevDuration = configManager.duration
2387
2402
  prevPeriod = configManager.period
2388
2403
  candleDuration = (userPassedArgs.intraday if (userPassedArgs is not None and userPassedArgs.intraday is not None) else ("1m" if configManager.duration.endswith("d") else configManager.duration))
2389
2404
  configManager.toggleConfig(candleDuration=candleDuration,clearCache=False)
2390
- if ":33:3:" in userPassedArgs.options:
2405
+ if userPassedArgs is not None and ":33:3:" in userPassedArgs.options:
2391
2406
  exists, cache_file = AssetsManager.PKAssetsManager.afterMarketStockDataExists(True, forceLoad=(menuOption in ["X", "B", "G", "S", "F"]))
2392
2407
  cache_file = os.path.join(Archiver.get_user_data_dir(),cache_file)
2393
2408
  cacheFileSize = os.stat(cache_file).st_size if os.path.exists(cache_file) else 0
@@ -933,6 +933,11 @@ def pkscreenercli():
933
933
  OutputControls().printOutput(e)
934
934
  traceback.print_exc()
935
935
  pass
936
+ # finally:
937
+ # import threading
938
+ # from pkscreener.globals import tryLoadDataOnBackgroundThread
939
+ # ping_thread = threading.Thread(target=tryLoadDataOnBackgroundThread, daemon=True)
940
+ # ping_thread.start()
936
941
  try:
937
942
  removeOldInstances()
938
943
  OutputControls(enableMultipleLineOutput=(args is None or args.monitor is None or args.runintradayanalysis),enableUserInput=(args is None or args.answerdefault is None)).printOutput("",end="\r")
@@ -19,7 +19,7 @@ pandas_ta #>=0.3.14b0
19
19
  # and https://github.com/pyinstaller/pyinstaller/issues/7269
20
20
  pefile>=2023.2.7,<2024.8.26
21
21
  Pillow #>=9.5.0,<=9.5.0 # Keep at this version because getsize_multiline is deprecated/removed in higher versions
22
- PKDevTools>=0.13.20250126.241
22
+ PKDevTools>=0.13.20250126.243
23
23
  PKNSETools>=0.1.20250122.139
24
24
  Pyarrow #>=17.0.0
25
25
  pyppeteer #>=2.0.0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pkscreener
3
- Version: 0.46.20250224.736
3
+ Version: 0.46.20250224.737
4
4
  Summary: A Python-based stock screener for NSE, India with alerts to Telegram Channel (pkscreener)
5
5
  Home-page: https://github.com/pkjmesra/pkscreener
6
- Download-URL: https://github.com/pkjmesra/pkscreener/archive/v0.46.20250224.736.zip
6
+ Download-URL: https://github.com/pkjmesra/pkscreener/archive/v0.46.20250224.737.zip
7
7
  Author: pkjmesra
8
8
  Author-email: pkjmesra@gmail.com
9
9
  License: OSI Approved (MIT)
@@ -33,7 +33,7 @@ Requires-Dist: pandas
33
33
  Requires-Dist: pandas_ta
34
34
  Requires-Dist: pefile<2024.8.26,>=2023.2.7
35
35
  Requires-Dist: Pillow
36
- Requires-Dist: PKDevTools>=0.13.20250126.241
36
+ Requires-Dist: PKDevTools>=0.13.20250126.243
37
37
  Requires-Dist: PKNSETools>=0.1.20250122.139
38
38
  Requires-Dist: Pyarrow
39
39
  Requires-Dist: pyppeteer
@@ -392,15 +392,15 @@ After you have finished the run, go to that copied path, zip the contents of the
392
392
  [MADE-IN-INDIA-badge]: https://img.shields.io/badge/MADE%20WITH%20%E2%9D%A4%20IN-INDIA-orange
393
393
  [MADE-IN-INDIA]: https://en.wikipedia.org/wiki/India
394
394
  [Windows-badge]: https://img.shields.io/badge/Windows-0078D6?logo=windows&logoColor=white
395
- [Windows]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250222.735/pkscreenercli.exe
395
+ [Windows]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250224.736/pkscreenercli.exe
396
396
  [Linux-badge_x64]: https://img.shields.io/badge/Linux(x64)-FCC624?logo=linux&logoColor=black
397
- [Linux_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250222.735/pkscreenercli_x64.bin
397
+ [Linux_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250224.736/pkscreenercli_x64.bin
398
398
  [Linux-badge_arm64]: https://img.shields.io/badge/Linux(arm64)-FCC624?logo=linux&logoColor=black
399
- [Linux_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250222.735/pkscreenercli_arm64.bin
399
+ [Linux_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250224.736/pkscreenercli_arm64.bin
400
400
  [Mac OS-badge_x64]: https://img.shields.io/badge/mac%20os(x64)-D3D3D3?logo=apple&logoColor=000000
401
- [Mac OS_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250222.735/pkscreenercli_x64.run
401
+ [Mac OS_x64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250224.736/pkscreenercli_x64.run
402
402
  [Mac OS-badge_arm64]: https://img.shields.io/badge/mac%20os(arm64)-D3D3D3?logo=apple&logoColor=000000
403
- [Mac OS_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250222.735/pkscreenercli_arm64.run
403
+ [Mac OS_arm64]: https://github.com/pkjmesra/PKScreener/releases/download/0.46.20250224.736/pkscreenercli_arm64.run
404
404
  [GitHub release (latest by date)-badge]: https://img.shields.io/github/v/release/pkjmesra/PKScreener
405
405
  [GitHub release (latest by date)]: https://github.com/pkjmesra/PKScreener/releases/latest
406
406
  [pypi-badge]: https://img.shields.io/pypi/v/pkscreener.svg?style=flat-square
@@ -15,7 +15,7 @@ pandas
15
15
  pandas_ta
16
16
  pefile<2024.8.26,>=2023.2.7
17
17
  Pillow
18
- PKDevTools>=0.13.20250126.241
18
+ PKDevTools>=0.13.20250126.243
19
19
  PKNSETools>=0.1.20250122.139
20
20
  Pyarrow
21
21
  pyppeteer
@@ -1 +0,0 @@
1
- VERSION='0.46.20250224.736'