pkscreener 0.46.20250810.756__cp310-cp310-macosx_13_0_arm64.whl → 0.46.20250908.766__cp310-cp310-macosx_13_0_arm64.whl
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.
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/LICENSE-Others.txt +1 -1
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/README.txt +6 -6
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/__init__.py +2 -2
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/AssetsManager.py +13 -29
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/Backtest.py +5 -5
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/CandlePatterns.py +23 -23
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/ConfigManager.py +13 -1
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/ConsoleUtility.py +1 -1
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/Fetcher.py +26 -20
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/ImageUtility.py +1 -1
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/MarketMonitor.py +6 -6
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/MarketStatus.py +3 -2
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/PKMarketOpenCloseAnalyser.py +14 -14
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/PKScanRunner.py +2 -2
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/Pktalib.py +36 -36
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/PortfolioXRay.py +5 -5
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/ScreeningStatistics.py +457 -445
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/StockScreener.py +47 -34
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/Utility.py +4 -3
- pkscreener-0.46.20250908.766.data/purelib/pkscreener/classes/__init__.py +1 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/globals.py +25 -23
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/pkscreenerbot.py +24 -21
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/pkscreenercli.py +9 -9
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/requirements.txt +3 -3
- {pkscreener-0.46.20250810.756.dist-info → pkscreener-0.46.20250908.766.dist-info}/METADATA +10 -11
- pkscreener-0.46.20250908.766.dist-info/RECORD +58 -0
- pkscreener-0.46.20250810.756.data/purelib/pkscreener/classes/__init__.py +0 -1
- pkscreener-0.46.20250810.756.dist-info/RECORD +0 -58
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/Disclaimer.txt +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/LICENSE.txt +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/LogoWM.txt +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/ArtTexts.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/Barometer.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/BaseScreeningStatistics.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/Changelog.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/ConsoleMenuUtility.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/GlobalStore.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/MenuOptions.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/Messenger.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/OtaUpdater.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/PKAnalytics.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/PKDataService.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/PKDemoHandler.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/PKPremiumHandler.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/PKScheduledTaskProgress.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/PKScheduler.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/PKSpreadsheets.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/PKTask.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/PKUserRegistration.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/Portfolio.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/StockSentiment.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/UserMenuChoicesHandler.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/WorkflowManager.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/classes/keys.py +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/courbd.ttf +0 -0
- {pkscreener-0.46.20250810.756.data → pkscreener-0.46.20250908.766.data}/purelib/pkscreener/pkscreener.ini +0 -0
- {pkscreener-0.46.20250810.756.dist-info → pkscreener-0.46.20250908.766.dist-info}/LICENSE +0 -0
- {pkscreener-0.46.20250810.756.dist-info → pkscreener-0.46.20250908.766.dist-info}/WHEEL +0 -0
- {pkscreener-0.46.20250810.756.dist-info → pkscreener-0.46.20250908.766.dist-info}/entry_points.txt +0 -0
- {pkscreener-0.46.20250810.756.dist-info → pkscreener-0.46.20250908.766.dist-info}/top_level.txt +0 -0
@@ -35,7 +35,7 @@ warnings.simplefilter("ignore", FutureWarning)
|
|
35
35
|
import pandas as pd
|
36
36
|
# from PKDevTools.classes.log import tracelog
|
37
37
|
# from PKDevTools.classes.PKTimer import PKTimer
|
38
|
-
from PKDevTools.classes import Archiver
|
38
|
+
from PKDevTools.classes import Archiver, log
|
39
39
|
from PKDevTools.classes.ColorText import colorText
|
40
40
|
from PKDevTools.classes.Fetcher import StockDataEmptyException
|
41
41
|
from PKDevTools.classes.SuppressOutput import SuppressOutput
|
@@ -51,6 +51,17 @@ class StockScreener:
|
|
51
51
|
self.isTradingTime = PKDateUtilities.isTradingTime()
|
52
52
|
self.configManager = None
|
53
53
|
|
54
|
+
def setupLogger(self, log_level):
|
55
|
+
if log_level > 0:
|
56
|
+
os.environ["PKDevTools_Default_Log_Level"] = str(log_level)
|
57
|
+
log.setup_custom_logger(
|
58
|
+
"pkscreener",
|
59
|
+
log_level,
|
60
|
+
trace=False,
|
61
|
+
log_file_path="pkscreener-logs.txt",
|
62
|
+
filter=None,
|
63
|
+
)
|
64
|
+
|
54
65
|
# @tracelog
|
55
66
|
def screenStocks(
|
56
67
|
self,
|
@@ -85,6 +96,7 @@ class StockScreener:
|
|
85
96
|
), "hostRef argument must not be None. It should be an instance of PKMultiProcessorClient"
|
86
97
|
if stock is None or len(stock) == 0:
|
87
98
|
return None
|
99
|
+
self.setupLogger(log_level=logLevel)
|
88
100
|
configManager = hostRef.configManager
|
89
101
|
self.configManager = configManager
|
90
102
|
screeningDictionary, saveDictionary = self.initResultDictionaries()
|
@@ -480,7 +492,7 @@ class StockScreener:
|
|
480
492
|
# Must-run, but only at the end
|
481
493
|
try:
|
482
494
|
if executeOption != 7 or (executeOption == 7 and respChartPattern != 7):
|
483
|
-
# Only 'doji' and 'inside' is internally implemented by
|
495
|
+
# Only 'doji' and 'inside' is internally implemented by pandas_ta_classic.
|
484
496
|
# Otherwise, for the rest of the candle patterns, they also need
|
485
497
|
# TA-Lib. So if TA-Lib is not available, it will throw exception
|
486
498
|
# We can live with no-patterns if user has not installed ta-lib
|
@@ -682,7 +694,7 @@ class StockScreener:
|
|
682
694
|
screener.validateCCI(
|
683
695
|
processedData, screeningDictionary, saveDictionary, minRSI, maxRSI
|
684
696
|
)
|
685
|
-
if isNotMonitoringDashboard and executeOption != 21 and backtestDuration == 0:
|
697
|
+
if configManager.enableAdditionalTrendFilters and isNotMonitoringDashboard and executeOption != 21 and backtestDuration == 0:
|
686
698
|
# We don't need to have MFI or fair value data for backtesting because those
|
687
699
|
# are anyways only available for days in the past.
|
688
700
|
# For executeOption 21, we'd have already got the mfiStake and fairValueDiff
|
@@ -719,28 +731,28 @@ class StockScreener:
|
|
719
731
|
# Capturing Ctr+C Here isn't a great idea
|
720
732
|
pass
|
721
733
|
except StockDataEmptyException as e: # pragma: no cover
|
722
|
-
|
723
|
-
|
734
|
+
if data is None or (data is not None and not data.isnull().values.all(axis=0)[0]):
|
735
|
+
hostRef.default_logger.debug(f"StockDataEmptyException:{stock}: {e}", exc_info=True)
|
724
736
|
pass
|
725
737
|
except ScreeningStatistics.EligibilityConditionNotMet as e: # pragma: no cover
|
726
|
-
|
727
|
-
|
738
|
+
if userArgsLog:
|
739
|
+
hostRef.default_logger.debug(f"EligibilityConditionNotMet:{stock}: {e}", exc_info=True)
|
728
740
|
pass
|
729
741
|
except ScreeningStatistics.NotNewlyListed as e: # pragma: no cover
|
730
|
-
|
731
|
-
|
742
|
+
if userArgsLog:
|
743
|
+
hostRef.default_logger.debug(f"NotNewlyListed:{stock}: {e}", exc_info=True)
|
732
744
|
pass
|
733
745
|
except ScreeningStatistics.NotAStageTwoStock as e: # pragma: no cover
|
734
|
-
|
735
|
-
|
746
|
+
if userArgsLog:
|
747
|
+
hostRef.default_logger.debug(f"NotAStageTwoStock:{stock}: {e}", exc_info=True)
|
736
748
|
pass
|
737
749
|
except ScreeningStatistics.NotEnoughVolumeAsPerConfig as e: # pragma: no cover
|
738
|
-
|
739
|
-
|
750
|
+
if userArgsLog:
|
751
|
+
hostRef.default_logger.debug(f"NotEnoughVolumeAsPerConfig:{stock}: {e}", exc_info=True)
|
740
752
|
pass
|
741
753
|
except ScreeningStatistics.DownloadDataOnly as e: # pragma: no cover
|
742
|
-
|
743
|
-
|
754
|
+
if userArgsLog:
|
755
|
+
hostRef.default_logger.debug(f"DownloadDataOnly:{stock}: {e}", exc_info=True)
|
744
756
|
try:
|
745
757
|
data = hostRef.objectDictionaryPrimary.get(stock)
|
746
758
|
if data is not None:
|
@@ -750,7 +762,7 @@ class StockScreener:
|
|
750
762
|
except KeyboardInterrupt: # pragma: no cover
|
751
763
|
raise KeyboardInterrupt
|
752
764
|
except Exception as ex:
|
753
|
-
|
765
|
+
hostRef.default_logger.debug(f"MFIStatus: {stock}:\n{ex}", exc_info=True)
|
754
766
|
pass
|
755
767
|
try:
|
756
768
|
screener.getFairValue(stock,hostData=data, force=True,exchangeName=exchangeName)
|
@@ -758,24 +770,24 @@ class StockScreener:
|
|
758
770
|
except KeyboardInterrupt: # pragma: no cover
|
759
771
|
raise KeyboardInterrupt
|
760
772
|
except Exception as ex:
|
761
|
-
|
773
|
+
hostRef.default_logger.debug(f"FairValue: {stock}:\n{ex}", exc_info=True)
|
762
774
|
pass
|
763
775
|
pass
|
764
776
|
except ScreeningStatistics.LTPNotInConfiguredRange as e: # pragma: no cover
|
765
|
-
|
766
|
-
|
777
|
+
if userArgsLog:
|
778
|
+
hostRef.default_logger.debug(f"LTPNotInConfiguredRange:{stock}: {e}", exc_info=True)
|
767
779
|
pass
|
768
780
|
except KeyError as e: # pragma: no cover
|
769
|
-
|
770
|
-
|
781
|
+
if userArgsLog:
|
782
|
+
hostRef.default_logger.debug(f"KeyError:{stock}: {e}", exc_info=True)
|
771
783
|
pass
|
772
784
|
except OSError as e: # pragma: no cover
|
773
|
-
|
774
|
-
|
785
|
+
if userArgsLog:
|
786
|
+
hostRef.default_logger.debug(f"OSError:{stock}: {e}", exc_info=True)
|
775
787
|
pass
|
776
788
|
except Exception as e: # pragma: no cover
|
777
|
-
|
778
|
-
|
789
|
+
if userArgsLog:
|
790
|
+
hostRef.default_logger.debug(f"Exception:{stock}: {e}", exc_info=True)
|
779
791
|
if testbuild or printCounter:
|
780
792
|
import traceback
|
781
793
|
traceback.print_exc()
|
@@ -905,19 +917,19 @@ class StockScreener:
|
|
905
917
|
fullData = None
|
906
918
|
processedData = None
|
907
919
|
ohlc_dict = {
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
920
|
+
"open":'first',
|
921
|
+
"high":'max',
|
922
|
+
"low":'min',
|
923
|
+
"close":'last',
|
912
924
|
'Adj Close': 'last',
|
913
|
-
|
925
|
+
"volume":'sum'
|
914
926
|
}
|
915
927
|
candleDuration = self.configManager.candleDurationInt
|
916
928
|
candleDurationFrequency = self.configManager.candleDurationFrequency
|
917
929
|
durationFrequency = "T" if candleDurationFrequency=="m" else ("H" if candleDurationFrequency=="h" else ("M" if candleDurationFrequency=="mo" else ("W" if candleDurationFrequency=="wk" else "T")))
|
918
930
|
if int(candleDuration) >= 1 and (candleDurationFrequency in ["m","h","mo","wk"]):
|
919
931
|
data = data.resample(f'{candleDuration}{durationFrequency}', offset='15min').agg(ohlc_dict)
|
920
|
-
data = data[data["
|
932
|
+
data = data[data["high"]>0] # resampling can introduce 0 value rows for non-market hours
|
921
933
|
if backtestDuration == 0:
|
922
934
|
fullData, processedData = screener.preprocessData(
|
923
935
|
data, daysToLookback=configManager.effectiveDaysToLookback
|
@@ -1049,6 +1061,7 @@ class StockScreener:
|
|
1049
1061
|
else:
|
1050
1062
|
data.rename(columns={"index": "Date"}, inplace=True)
|
1051
1063
|
data.set_index("Date", inplace=True)
|
1064
|
+
data.index = pd.to_datetime(data.index)
|
1052
1065
|
except: # pragma: no cover
|
1053
1066
|
pass
|
1054
1067
|
if ((shouldCache and not self.isTradingTime and (hostData is None or hostDataLength == 0)) or downloadOnly) \
|
@@ -1130,7 +1143,7 @@ class StockScreener:
|
|
1130
1143
|
"52Wk-L",
|
1131
1144
|
"RSI",
|
1132
1145
|
"RSIi",
|
1133
|
-
"
|
1146
|
+
"volume",
|
1134
1147
|
"22-Pd",
|
1135
1148
|
"Consol.",
|
1136
1149
|
"Breakout",
|
@@ -1148,7 +1161,7 @@ class StockScreener:
|
|
1148
1161
|
"52Wk-L": 0,
|
1149
1162
|
"RSI": 0,
|
1150
1163
|
"RSIi": 0,
|
1151
|
-
"
|
1164
|
+
"volume": "",
|
1152
1165
|
"22-Pd": "",
|
1153
1166
|
"Consol.": "Range:0%",
|
1154
1167
|
"Breakout": "BO: 0 R: 0",
|
@@ -1166,7 +1179,7 @@ class StockScreener:
|
|
1166
1179
|
"52Wk-L": 0,
|
1167
1180
|
"RSI": 0,
|
1168
1181
|
"RSIi": 0,
|
1169
|
-
"
|
1182
|
+
"volume": "",
|
1170
1183
|
"22-Pd": "",
|
1171
1184
|
"Consol.": "Range:0%",
|
1172
1185
|
"Breakout": "BO: 0 R: 0",
|
@@ -73,6 +73,7 @@ artText = f"{getArtText()}\n"
|
|
73
73
|
STD_ENCODING=sys.stdout.encoding if sys.stdout is not None else 'utf-8'
|
74
74
|
|
75
75
|
def marketStatus():
|
76
|
+
return "NA"
|
76
77
|
# task = PKTask("Nifty 50 Market Status",MarketStatus().getMarketStatus)
|
77
78
|
lngStatus = MarketStatus().marketStatus
|
78
79
|
nseStatus = ""
|
@@ -85,8 +86,8 @@ def marketStatus():
|
|
85
86
|
# scheduleTasks(tasksList=[task])
|
86
87
|
if lngStatus == "":
|
87
88
|
lngStatus = MarketStatus().getMarketStatus(exchangeSymbol="^IXIC" if configManager.defaultIndex == 15 else "^NSEI")
|
88
|
-
if "
|
89
|
-
lngStatus = lngStatus.replace("Closed","
|
89
|
+
if "close" in lngStatus and nseStatus == "open":
|
90
|
+
lngStatus = lngStatus.replace("Closed","open")
|
90
91
|
if len(next_bell) > 0 and next_bell not in lngStatus:
|
91
92
|
lngStatus = f"{lngStatus} | Next Bell: {colorText.WARN}{next_bell.replace('T',' ').split('+')[0]}{colorText.END}"
|
92
93
|
return (lngStatus +"\n") if lngStatus is not None else "\n"
|
@@ -141,7 +142,7 @@ class tools:
|
|
141
142
|
pass
|
142
143
|
|
143
144
|
@Halo(text='', spinner='dots')
|
144
|
-
def tryFetchFromServer(cache_file,repoOwner="pkjmesra",repoName="PKScreener",directory="
|
145
|
+
def tryFetchFromServer(cache_file,repoOwner="pkjmesra",repoName="PKScreener",directory="results/Data",hideOutput=False,branchName="refs/heads/actions-data-download"):
|
145
146
|
if not hideOutput:
|
146
147
|
OutputControls().printOutput(
|
147
148
|
colorText.FAIL
|
@@ -0,0 +1 @@
|
|
1
|
+
VERSION='0.46.20250908.766'
|
@@ -320,7 +320,7 @@ def getSummaryCorrectnessOfStrategy(resultdf, summaryRequired=True):
|
|
320
320
|
)
|
321
321
|
)
|
322
322
|
detaildf = detaildf.replace(np.nan, "", regex=True)
|
323
|
-
detaildf.loc[:, "
|
323
|
+
detaildf.loc[:, "volume"] = detaildf.loc[:, "volume"].apply(
|
324
324
|
lambda x: Utility.tools.formatRatio(x, configManager.volumeRatio)
|
325
325
|
)
|
326
326
|
detaildf.sort_values(
|
@@ -754,7 +754,7 @@ def labelDataForPrinting(screenResults, saveResults, configManager, volumeRatio,
|
|
754
754
|
saveResults['RSI'] = saveResults['RSI'].astype(str) + "/" + saveResults['RSIi'].astype(str)
|
755
755
|
screenResults.rename(columns={"RSI": "RSI/i"},inplace=True)
|
756
756
|
saveResults.rename(columns={"RSI": "RSI/i"},inplace=True)
|
757
|
-
sortKey = ["
|
757
|
+
sortKey = ["volume"] if "RSI" not in menuChoiceHierarchy else ("RSIi" if (isTrading or "RSIi" in saveResults.columns) else "RSI")
|
758
758
|
ascending = [False if "RSI" not in menuChoiceHierarchy else True]
|
759
759
|
if executeOption == 21:
|
760
760
|
if reversalOption in [3,5,6,7]:
|
@@ -769,20 +769,20 @@ def labelDataForPrinting(screenResults, saveResults, configManager, volumeRatio,
|
|
769
769
|
sortKey = ["SuperConfSort"]
|
770
770
|
ascending = [False]
|
771
771
|
else:
|
772
|
-
sortKey = ["
|
772
|
+
sortKey = ["volume"]
|
773
773
|
ascending = [False]
|
774
774
|
elif reversalOption in [4]:
|
775
775
|
if "deviationScore" in saveResults.columns:
|
776
776
|
sortKey = ["deviationScore"]
|
777
777
|
ascending = [True]
|
778
778
|
else:
|
779
|
-
sortKey = ["
|
779
|
+
sortKey = ["volume"]
|
780
780
|
ascending = [False]
|
781
781
|
elif executeOption == 23:
|
782
|
-
sortKey = ["bbands_ulr_ratio_max5"] if "bbands_ulr_ratio_max5" in screenResults.columns else ["
|
782
|
+
sortKey = ["bbands_ulr_ratio_max5"] if "bbands_ulr_ratio_max5" in screenResults.columns else ["volume"]
|
783
783
|
ascending = [False]
|
784
784
|
elif executeOption == 27: # ATR Cross
|
785
|
-
sortKey = ["ATR"] if "ATR" in screenResults.columns else ["
|
785
|
+
sortKey = ["ATR"] if "ATR" in screenResults.columns else ["volume"]
|
786
786
|
ascending = [False]
|
787
787
|
elif executeOption == 31: # DEEL Momentum
|
788
788
|
sortKey = ["%Chng"]
|
@@ -824,12 +824,12 @@ def labelDataForPrinting(screenResults, saveResults, configManager, volumeRatio,
|
|
824
824
|
screenResults.set_index("Stock", inplace=True)
|
825
825
|
if "Stock" in saveResults.columns:
|
826
826
|
saveResults.set_index("Stock", inplace=True)
|
827
|
-
screenResults[
|
828
|
-
saveResults[
|
829
|
-
screenResults.loc[:, "
|
827
|
+
screenResults["volume"] = screenResults["volume"].astype(str)
|
828
|
+
saveResults["volume"] = saveResults["volume"].astype(str)
|
829
|
+
screenResults.loc[:, "volume"] = screenResults.loc[:, "volume"].apply(
|
830
830
|
lambda x: Utility.tools.formatRatio(float(ImageUtility.PKImageTools.removeAllColorStyles(x)), volumeRatio) if len(str(x).strip()) > 0 else ''
|
831
831
|
)
|
832
|
-
saveResults.loc[:, "
|
832
|
+
saveResults.loc[:, "volume"] = saveResults.loc[:, "volume"].apply(
|
833
833
|
lambda x: str(x) + "x"
|
834
834
|
)
|
835
835
|
screenResults.rename(
|
@@ -1823,7 +1823,7 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
|
|
1823
1823
|
prediction, pText, sText = screener.getNiftyPrediction(
|
1824
1824
|
df=fetcher.fetchLatestNiftyDaily(proxyServer=fetcher.proxyServer)
|
1825
1825
|
)
|
1826
|
-
warningText = "\nNifty AI prediction works best if you request after market is closed. It may not be accurate while market is still open!" if "
|
1826
|
+
warningText = "\nNifty AI prediction works best if you request after market is closed. It may not be accurate while market is still open!" if "open" in Utility.marketStatus() else ""
|
1827
1827
|
try:
|
1828
1828
|
todayHoliday, todayOccassion = PKDateUtilities.isHoliday(PKDateUtilities.currentDateTime())
|
1829
1829
|
nextWeekday = PKDateUtilities.nextWeekday()
|
@@ -1916,10 +1916,12 @@ def main(userArgs=None,optionalFinalOutcome_df=None):
|
|
1916
1916
|
pass
|
1917
1917
|
loadCount = len(stockDictPrimary) if stockDictPrimary is not None else 0
|
1918
1918
|
# Let's use screening only for the stocks for which we could get the data.
|
1919
|
-
savedOrDownloadedKeys = list(stockDictPrimary.keys())
|
1920
|
-
missingStocks = set(listStockCodes) - set(savedOrDownloadedKeys)
|
1921
|
-
|
1922
|
-
|
1919
|
+
savedOrDownloadedKeys = listStockCodes if (userArgs.options is not None and "," in userArgs.options) else list(stockDictPrimary.keys())
|
1920
|
+
missingStocks = set(listStockCodes) - set([ x.replace("-BE","").replace("-BZ","") for x in savedOrDownloadedKeys ])
|
1921
|
+
# print(missingStocks)
|
1922
|
+
# default_logger().debug(missingStocks)
|
1923
|
+
OutputControls().printOutput(f"{colorText.GREEN} [+] Adding {len(savedOrDownloadedKeys)-len(missingStocks)} stocks out of {len(savedOrDownloadedKeys)} to the queue...{colorText.END}")
|
1924
|
+
listStockCodes = list(set(savedOrDownloadedKeys)-set(missingStocks)) if not downloadOnly else savedOrDownloadedKeys
|
1923
1925
|
if downloadOnly:
|
1924
1926
|
OutputControls().printOutput(
|
1925
1927
|
colorText.WARN
|
@@ -2336,7 +2338,7 @@ def analysisFinalResults(screenResults,saveResults,optionalFinalOutcome_df,runOp
|
|
2336
2338
|
analysis_df = screenResults.copy()
|
2337
2339
|
else:
|
2338
2340
|
analysis_df = pd.DataFrame()
|
2339
|
-
index_columns = ["Stock","%Chng","
|
2341
|
+
index_columns = ["Stock","%Chng","volume","Pattern","MA-Signal","Trend(22Prds)","Trend","LTP","LTP@Alert","AlertTime","SqrOff","SqrOffLTP","SqrOffDiff","EoDDiff","DayHighTime","DayHigh","DayHighDiff"]
|
2340
2342
|
final_index_columns = []
|
2341
2343
|
firstScanKey = userPassedArgs.options.split(">|")[0]
|
2342
2344
|
for column in index_columns:
|
@@ -2474,7 +2476,7 @@ def FinishBacktestDataCleanup(backtest_df, df_xray):
|
|
2474
2476
|
"22": "22-Pd",
|
2475
2477
|
"30": "30-Pd",
|
2476
2478
|
"T": "Trend",
|
2477
|
-
"V": "
|
2479
|
+
"V": "volume",
|
2478
2480
|
"M": "MA-Signal",
|
2479
2481
|
}
|
2480
2482
|
if configManager.enablePortfolioCalculations:
|
@@ -3259,17 +3261,17 @@ def printNotifySaveScreenedResults(
|
|
3259
3261
|
with pd.option_context('mode.chained_assignment', None):
|
3260
3262
|
caption_df.rename(columns={"DayHighDiff": "Hgh","EoDDiff":"EoD"}, inplace=True)
|
3261
3263
|
else:
|
3262
|
-
caption_df = saveResultsTrimmed[['LTP','%Chng',
|
3264
|
+
caption_df = saveResultsTrimmed[['LTP','%Chng',"volume"]].head(configManager.telegramSampleNumberRows)
|
3263
3265
|
caption_df.loc[:, "LTP"] = caption_df.loc[:, "LTP"].apply(
|
3264
3266
|
lambda x: str(int(round(float(x),0)))
|
3265
3267
|
)
|
3266
3268
|
caption_df.loc[:, "%Chng"] = caption_df.loc[:, "%Chng"].apply(
|
3267
3269
|
lambda x: f'{int(round(float(x.split(" ")[0].replace("%","")),0))}%'
|
3268
3270
|
)
|
3269
|
-
caption_df.loc[:, "
|
3271
|
+
caption_df.loc[:, "volume"] = caption_df.loc[:, "volume"].apply(
|
3270
3272
|
lambda x: f'{int(round(float(x.replace("x","")),0))}x' if (len(x.replace("x","").strip()) > 0 and not pd.isna(float(x.replace("x","")))) else ''
|
3271
3273
|
)
|
3272
|
-
caption_df.rename(columns={"%Chng": "Ch%","
|
3274
|
+
caption_df.rename(columns={"%Chng": "Ch%","volume":"Vol"}, inplace=True)
|
3273
3275
|
except: # pragma: no cover
|
3274
3276
|
cols = [list(saveResultsTrimmed.columns)[0]]
|
3275
3277
|
cols.extend(list(saveResultsTrimmed.columns[5:]))
|
@@ -3285,7 +3287,7 @@ def printNotifySaveScreenedResults(
|
|
3285
3287
|
).encode("utf-8").decode(STD_ENCODING).replace("-K-----S-----C-----R","-K-----S----C---R").replace("% ","% ").replace("=K=====S=====C=====R","=K=====S====C===R").replace("Vol |","Vol|").replace("Hgh |","Hgh|").replace("EoD |","EoD|").replace("x ","x")
|
3286
3288
|
caption_results = ImageUtility.PKImageTools.removeAllColorStyles(caption_results.replace("-E-----N-----E-----R","-E-----N----E---R").replace("=E=====N=====E=====R","=E=====N====E===R"))
|
3287
3289
|
suggestion_text = "Try @nse_pkscreener_bot for more scans! <i><b><u>You agree that you have read</u></b>:https://pkjmesra.github.io/PKScreener/Disclaimer.txt</i> <b>and accept TOS</b>: https://pkjmesra.github.io/PKScreener/tos.txt <b>STOP using and exit from channel/group, if you do not</b>"
|
3288
|
-
finalCaption = f"{caption}.Feel free to share on social media.
|
3290
|
+
finalCaption = f"{caption}.Feel free to share on social media.open attached image for more. Samples:<pre>{caption_results}</pre>{elapsed_text} {suggestion_text}"
|
3289
3291
|
if not testing:
|
3290
3292
|
if PKDateUtilities.isTradingTime() and not PKDateUtilities.isTodayHoliday()[0]:
|
3291
3293
|
kite_file_path, kite_caption = sendKiteBasketOrderReviewDetails(saveResultsTrimmed,runOptionName,caption,user)
|
@@ -3705,11 +3707,11 @@ def runScanners(
|
|
3705
3707
|
if result is not None:
|
3706
3708
|
if not userPassedArgs.monitor and len(lstscreen) > 0 and userPassedArgs is not None and userPassedArgs.options.split(":")[2] in ["29"]:
|
3707
3709
|
scr_df = pd.DataFrame(lstscreen)
|
3708
|
-
existingColumns = ["Stock","%Chng","LTP","
|
3710
|
+
existingColumns = ["Stock","%Chng","LTP","volume"]
|
3709
3711
|
newColumns = ["BidQty","AskQty","LwrCP","UprCP","VWAP","DayVola","Del(%)"]
|
3710
3712
|
existingColumns.extend(newColumns)
|
3711
3713
|
scr_df = scr_df[existingColumns]
|
3712
|
-
scr_df.sort_values(by=["
|
3714
|
+
scr_df.sort_values(by=["volume","BidQty"], ascending=False, inplace=True)
|
3713
3715
|
tabulated_results = colorText.miniTabulator().tb.tabulate(
|
3714
3716
|
scr_df,
|
3715
3717
|
headers="keys",
|
@@ -61,6 +61,7 @@ MINUTES_2_IN_SECONDS = 120
|
|
61
61
|
OWNER_USER = "Itsonlypk"
|
62
62
|
APOLOGY_TEXT = "Apologies! The @nse_pkscreener_bot is NOT available for the time being! We are working with our host GitHub and other data source providers to sort out pending invoices and restore the services soon! Thanks for your patience and support! 🙏"
|
63
63
|
|
64
|
+
from pkscreener.classes import VERSION
|
64
65
|
from PKDevTools.classes.Environment import PKEnvironment
|
65
66
|
from PKDevTools.classes.PKDateUtilities import PKDateUtilities
|
66
67
|
from PKDevTools.classes.ColorText import colorText
|
@@ -142,6 +143,7 @@ m3 = menus()
|
|
142
143
|
m4 = menus()
|
143
144
|
int_timer = None
|
144
145
|
_updater = None
|
146
|
+
QR_CODE_PAYMENT_LINK="upi://pay?pa=PKSCREENER@APL&pn=PKSCREENER&tn=undefined&am=undefined"
|
145
147
|
|
146
148
|
TOP_LEVEL_SCANNER_MENUS = ["X", "B", "MI","DV", "P"] #
|
147
149
|
TOP_LEVEL_SCANNER_SKIP_MENUS = ["M", "S", "F", "G", "C", "T", "D", "I", "E", "U", "L", "Z", "P"] # Last item will be skipped.
|
@@ -278,7 +280,7 @@ def matchUTR(update: Update, context: CallbackContext) -> str:
|
|
278
280
|
if len(args) > 0: # UTR
|
279
281
|
matchedTran = PKGmailReader.matchUTR(utr=args[0])
|
280
282
|
if matchedTran is not None:
|
281
|
-
updatedResults = f"We have found the following transaction for the provided UTR:\n{matchedTran}\
|
283
|
+
updatedResults = f"We have found the following transaction for the provided UTR:\n{matchedTran}\nYour subscription is being enabled soon!\nPlease check with /OTP in the next couple of minutes!\nThank you for trusting PKScreener!"
|
282
284
|
try:
|
283
285
|
results = updateSubscription(user.id,int(float(matchedTran.get("amountPaid"))))
|
284
286
|
if results is not None:
|
@@ -288,15 +290,16 @@ def matchUTR(update: Update, context: CallbackContext) -> str:
|
|
288
290
|
updatedResults = f"{updatedResults} Uh oh! We ran into a problem enabling your subscription.\nPlease reach out to @ItsOnlyPK to resolve."
|
289
291
|
pass
|
290
292
|
else:
|
291
|
-
updatedResults = "We could not find any transaction details with the provided UTR.\nUPI transaction reference number is a 12-digit alphanumeric/numeric code that serves as a unique identifier for transactions. It is also known as the Unique Transaction Reference (UTR) number.\nYou can find your UPI reference number in the UPI-enabled app you used to make the transaction.\nFor example, you can find your UPI reference number in the History section of Google Pay. \nIn the Paytm app, you can find it by clicking View Details.\
|
293
|
+
updatedResults = "We could not find any transaction details with the provided UTR.\nUPI transaction reference number is a 12-digit alphanumeric/numeric code that serves as a unique identifier for transactions. It is also known as the Unique Transaction Reference (UTR) number.\nYou can find your UPI reference number in the UPI-enabled app you used to make the transaction.\nFor example, you can find your UPI reference number in the History section of Google Pay. \nIn the Paytm app, you can find it by clicking View Details.\nIf you still cannot find it, please drop a message with transaction details/snapshot to @ItsOnlyPK to enable subscription."
|
292
294
|
else:
|
293
|
-
updatedResults = "Did you forget to include the UTR number with /Check ?\nYou should use it like this:\n
|
295
|
+
updatedResults = "Did you forget to include the UTR number with /Check ?\nYou should use it like this:\n/Check UTR_Here\nUPI transaction reference number is a 12-digit alphanumeric/numeric code that serves as a unique identifier for transactions. It is also known as the Unique Transaction Reference (UTR) number.\nYou can find your UPI reference number in the UPI-enabled app you used to make the transaction.\nFor example, you can find your UPI reference number in the History section of Google Pay. \nIn the Paytm app, you can find it by clicking View Details.\nIf you still cannot find it, please drop a message with transaction details/snapshot to @ItsOnlyPK to enable subscription."
|
294
296
|
update.message.reply_text(sanitiseTexts(updatedResults), reply_markup=default_markup(user=user),parse_mode="HTML")
|
295
297
|
shareUpdateWithChannel(update=update, context=context, optionChoices=f"/otp\n{updatedResults}")
|
296
298
|
return START_ROUTES
|
297
299
|
|
298
300
|
def editMessageText(query,editedText,reply_markup):
|
299
|
-
|
301
|
+
# .replace(microsecond=0).isoformat()
|
302
|
+
editedText = f"PKScreener <b>v{VERSION}</b>\n{PKDateUtilities.currentDateTime()}:\n{editedText}"
|
300
303
|
if query is not None and hasattr(query, "edit_message_text"):
|
301
304
|
query.edit_message_text(text=editedText, reply_markup=reply_markup,parse_mode="HTML")
|
302
305
|
|
@@ -359,7 +362,7 @@ def start(update: Update, context: CallbackContext, updatedResults=None, monitor
|
|
359
362
|
menuText = f"{menuText}\nClick /start if you want to restart the session."
|
360
363
|
else:
|
361
364
|
if not isUserSubscribed(user):
|
362
|
-
updatedResults = f"Thank you for choosing Intraday Monitor!\
|
365
|
+
updatedResults = f"Thank you for choosing Intraday Monitor!\nThis scan request is, however, protected and is only available to premium subscribers. It seems like you are not subscribed to the paid/premium subscription to PKScreener.\nPlease checkout all premium options by sending out a request:\n/OTP\nFor basic/unpaid users, you can try out the following:\n/X_0 StockCode1,StockCode2,etc.\n/X_N\n/X_1"
|
363
366
|
updatedResults = f"{updatedResults}\nClick /start if you want to restart the session."
|
364
367
|
chosenBotMenuOption = f"{chosenBotMenuOption}\nInt. Monitor. MonitorIndex:{monitorIndex}\n{updatedResults}"
|
365
368
|
menuText = updatedResults
|
@@ -665,7 +668,7 @@ def viewSubscriptionOptions(update:Update,context:CallbackContext,sendOTP=False)
|
|
665
668
|
subscriptionModelNames = f"{subscriptionModelNames}\n₹ {str(value).ljust(6)}: {name} (Only Basic Scans are free)\n"
|
666
669
|
else:
|
667
670
|
subscriptionModelNames = f"{subscriptionModelNames}\n₹ {str(value).ljust(6)}: {name}"
|
668
|
-
subscriptionModelNames = f"{subscriptionModelNames}</pre>\nPlease pay to subscribe:\n1. Using UPI(India) to <a href='https://tinyurl.com/v7h3t233'>PKScreener@APL</a> or\n2. Proudly <a href='https://github.com/sponsors/pkjmesra?frequency=recurring&sponsor=pkjmesra'><b>sponsor</b></a>\
|
671
|
+
subscriptionModelNames = f"{subscriptionModelNames}</pre>\nPlease pay to subscribe:\n1. Using UPI(India) to <a href='https://tinyurl.com/v7h3t233'>PKScreener@APL</a> or\n2. Proudly <a href='https://github.com/sponsors/pkjmesra?frequency=recurring&sponsor=pkjmesra'><b>sponsor</b></a>\nPlease send\n/check UPI_UTR_HERE_After_Making_Payment to share transaction reference number to automatically enable subscription after making payment via UPI\n. If it is not auto-enabled, please drop a message to @ItsOnlyPK on Telegram after paying to enable subscription manually."
|
669
672
|
|
670
673
|
subscriptionModelName = PKUserSusbscriptions().subscriptionValueKeyPairs[subsModel]
|
671
674
|
if subscriptionModelName != PKSubscriptionModel.No_Subscription.name:
|
@@ -678,7 +681,7 @@ def viewSubscriptionOptions(update:Update,context:CallbackContext,sendOTP=False)
|
|
678
681
|
if otpValue == 0:
|
679
682
|
updatedResults = f"We are having difficulty generating OTP for your {userText}. Please try again later or reach out to @ItsOnlyPK."
|
680
683
|
else:
|
681
|
-
updatedResults = f"Please use the following to login to PKScreener:\n{userText}\n<b>OTP</b> : <code>{otpValue}</code>\
|
684
|
+
updatedResults = f"Please use the following to login to PKScreener:\n{userText}\n<b>OTP</b> : <code>{otpValue}</code>\nCurrent subscription : <b>{subscriptionModelName}</b>.\nCurrent alerts balance: <b>₹ {alertUser.balance if alertUser is not None else 0}</b> {scannerJobsSubscribed}. {subscriptionModelNames}"
|
682
685
|
else:
|
683
686
|
updatedResults = f"Current subscription: <b>{subscriptionModelName}</b>.\nCurrent alerts balance: <b>₹ {alertUser.balance if alertUser is not None else 0}</b> {scannerJobsSubscribed}. {subscriptionModelNames}"
|
684
687
|
|
@@ -733,7 +736,7 @@ def subscribeToScannerAlerts(update: Update, context: CallbackContext) -> str:
|
|
733
736
|
menuText = ""
|
734
737
|
requiredBalance = 40 if str(scanId).upper().startswith("P") else 31
|
735
738
|
# upi://pay?pa=PKScreener@APL&pn=PKScreener&cu=INR
|
736
|
-
payWall = "Please pay to subscribe:\n1. Using UPI(India) to <a href='https://tinyurl.com/v7h3t233'>PKScreener@APL</a> or\n2. Proudly <a href='https://github.com/sponsors/pkjmesra?frequency=recurring&sponsor=pkjmesra'>sponsor</a>\
|
739
|
+
payWall = "Please pay to subscribe:\n1. Using UPI(India) to <a href='https://tinyurl.com/v7h3t233'>PKScreener@APL</a> or\n2. Proudly <a href='https://github.com/sponsors/pkjmesra?frequency=recurring&sponsor=pkjmesra'>sponsor</a>\nPlease use\n/check UPI_UTR_HERE_After_Making_Payment to share transaction reference number to automatically update your balance after making payment via UPI.\nAfter that you can try re-subscribing!\nIf you still face any problem, please drop a message to @ItsOnlyPK along with UTR and Scan details on Telegram after paying to enable subscription manually."
|
737
740
|
if alertUser is not None and alertUser.balance >= 0:
|
738
741
|
# User has some balance
|
739
742
|
if len(alertUser.scannerJobs) > 0:
|
@@ -1249,7 +1252,7 @@ def Level2(update: Update, context: CallbackContext) -> str:
|
|
1249
1252
|
)
|
1250
1253
|
optionChoices = f"{optionChoices}{f' > {selection[4]}' if len(selection) > 4 else ''}".replace(" > >","").strip()
|
1251
1254
|
expectedTime = f"{'10 to 15' if '> 15' in optionChoices else '1 to 2'}"
|
1252
|
-
menuText = f"Thank you for choosing {optionChoices.replace(' > > ','')}. You will receive the notification/results in about {expectedTime} minutes. It generally takes 1-2 minutes for NSE (2000+) stocks and 10-15 minutes for NASDAQ (7300+).\
|
1255
|
+
menuText = f"Thank you for choosing {optionChoices.replace(' > > ','')}. You will receive the notification/results in about {expectedTime} minutes. It generally takes 1-2 minutes for NSE (2000+) stocks and 10-15 minutes for NASDAQ (7300+).\nPKScreener had been free for a long time, but owing to cost/budgeting issues, only a basic set of features will always remain free for everyone. Consider donating to help cover the basic server costs or subscribe to premium, if not subscribed yet:\nUPI (India): <a href='https://tinyurl.com/v7h3t233'>PKScreener@APL</a>\nor <a href='https://github.com/sponsors/pkjmesra?frequency=recurring&sponsor=pkjmesra'>sponsor</a>"
|
1253
1256
|
|
1254
1257
|
reply_markup = default_markup(user=user)
|
1255
1258
|
options = ":".join(selection)
|
@@ -1445,7 +1448,7 @@ def launchScreener(options, user, context, optionChoices, update):
|
|
1445
1448
|
isBasicScanRequest = True
|
1446
1449
|
break
|
1447
1450
|
if not isBasicScanRequest:
|
1448
|
-
responseText = f"Thank you for choosing {scanRequest}!\
|
1451
|
+
responseText = f"Thank you for choosing {scanRequest}!\nThis {'Backtest' if str(scanRequest).startswith('B') else 'Scan'} request is, however, protected and is only available to premium subscribers. It seems like you are not subscribed to the paid/premium subscription to PKScreener.\nPlease checkout all premium options by sending out a request:\n/OTP\nFor basic/unpaid users, you can try out the following:\n/X_0 StockCode1,StockCode2,etc.\n/X_N\n/X_1\n"
|
1449
1452
|
if update is not None and update.message is not None:
|
1450
1453
|
update.message.reply_text(sanitiseTexts(responseText),reply_markup=default_markup(user=user),parse_mode="HTML")
|
1451
1454
|
else:
|
@@ -1461,10 +1464,10 @@ def launchScreener(options, user, context, optionChoices, update):
|
|
1461
1464
|
optionChoices = optionChoices[:-1]
|
1462
1465
|
if str(optionChoices).split("_")[2] == "6" and str(optionChoices).split("_")[3] == "7":
|
1463
1466
|
optionChoices = f"{optionChoices}_3" # Lorenzian Any/All
|
1464
|
-
responseText = f"Thank you for choosing {optionChoices}!\
|
1465
|
-
responseText = f"{responseText}\
|
1466
|
-
responseText = f"{responseText}\
|
1467
|
-
responseText = f"{responseText}\
|
1467
|
+
responseText = f"Thank you for choosing {optionChoices}!\nHere are the results:\nInsights: https://pkjmesra.github.io/PKScreener/Backtest-Reports/PKScreener_{optionChoices}_Insights_DateSorted.html"
|
1468
|
+
responseText = f"{responseText}\nSummary: https://pkjmesra.github.io/PKScreener/Backtest-Reports/PKScreener_{optionChoices}_Summary_StockSorted.html"
|
1469
|
+
responseText = f"{responseText}\nStock-wise: https://pkjmesra.github.io/PKScreener/Backtest-Reports/PKScreener_{optionChoices}_backtest_result_StockSorted.html"
|
1470
|
+
responseText = f"{responseText}\nOther Reports: https://pkjmesra.github.io/PKScreener/BacktestReports.html"
|
1468
1471
|
if update is not None and update.message is not None:
|
1469
1472
|
update.message.reply_text(sanitiseTexts(responseText),reply_markup=default_markup(user=user),parse_mode="HTML")
|
1470
1473
|
else:
|
@@ -1535,7 +1538,7 @@ def BBacktests(update: Update, context: CallbackContext) -> str:
|
|
1535
1538
|
]
|
1536
1539
|
]
|
1537
1540
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
1538
|
-
responseText = "Backtesting NOT implemented yet in this Bot!\
|
1541
|
+
responseText = "Backtesting NOT implemented yet in this Bot!\nYou can use backtesting by downloading the software from https://github.com/pkjmesra/PKScreener/"
|
1539
1542
|
responseText = f"{responseText}\nClick /start if you want to restart the session."
|
1540
1543
|
editMessageText(query=query,editedText=sanitiseTexts(responseText),reply_markup=default_markup(user=user))
|
1541
1544
|
registerUser(user)
|
@@ -1571,7 +1574,7 @@ def end(update: Update, context: CallbackContext) -> str:
|
|
1571
1574
|
"""
|
1572
1575
|
query = update.callback_query
|
1573
1576
|
query.answer()
|
1574
|
-
responseText = "See https://github.com/pkjmesra/PKScreener/ for more details or join https://t.me/PKScreener. \
|
1577
|
+
responseText = "See https://github.com/pkjmesra/PKScreener/ for more details or join https://t.me/PKScreener. \nSee you next time!"
|
1575
1578
|
responseText = f"{responseText}\nClick /start if you want to restart the session."
|
1576
1579
|
editMessageText(query=query,editedText=sanitiseTexts(responseText),reply_markup=default_markup(query.from_user))
|
1577
1580
|
return ConversationHandler.END
|
@@ -1635,9 +1638,9 @@ def error_handler(update: object, context: CallbackContext) -> None:
|
|
1635
1638
|
message = (
|
1636
1639
|
f"An exception was raised while handling an update\n"
|
1637
1640
|
f"<pre>update = {html.escape(json.dumps(update_str, indent=2, ensure_ascii=False))}"
|
1638
|
-
"</pre>\n
|
1639
|
-
f"<pre>context.chat_data = {html.escape(str(context.chat_data))}</pre>\n
|
1640
|
-
f"<pre>context.user_data = {html.escape(str(context.user_data))}</pre>\n
|
1641
|
+
"</pre>\n"
|
1642
|
+
f"<pre>context.chat_data = {html.escape(str(context.chat_data))}</pre>\n"
|
1643
|
+
f"<pre>context.user_data = {html.escape(str(context.user_data))}</pre>\n"
|
1641
1644
|
f"<pre>{html.escape(tb_string)}</pre>"
|
1642
1645
|
)
|
1643
1646
|
|
@@ -2030,7 +2033,7 @@ def sendRequestSubmitted(optionChoices, update, context):
|
|
2030
2033
|
return
|
2031
2034
|
# Get user that sent /start and log his name
|
2032
2035
|
user = updateCarrier.from_user
|
2033
|
-
menuText = f"Thank you for choosing {optionChoices}. You will receive the notification/results in about 1-2 minutes! \
|
2036
|
+
menuText = f"Thank you for choosing {optionChoices}. You will receive the notification/results in about 1-2 minutes! \nConsider donating to help keep this project going:\nUPI: <a href='https://tinyurl.com/v7h3t233'>PKScreener@APL</a> \nor <a href='https://github.com/sponsors/pkjmesra?frequency=recurring&sponsor=pkjmesra'>sponsor</a>"
|
2034
2037
|
update.message.reply_text(sanitiseTexts(menuText),reply_markup=default_markup(user=user),parse_mode="HTML")
|
2035
2038
|
# help_command(update=update, context=context)
|
2036
2039
|
shareUpdateWithChannel(
|
@@ -2097,7 +2100,7 @@ def help_command(update: Update, context: CallbackContext) -> None:
|
|
2097
2100
|
"""Send a message when the command /help is issued."""
|
2098
2101
|
if update is not None and update.message is not None:
|
2099
2102
|
update.message.reply_text(
|
2100
|
-
sanitiseTexts(f"You can begin by typing in /start (Recommended) and hit send!\nOR\nChoose an option:\n{cmdText}\
|
2103
|
+
sanitiseTexts(f"You can begin by typing in /start (Recommended) and hit send!\nOR\nChoose an option:\n{cmdText}\nWe recommend you start by clicking on this /start"),
|
2101
2104
|
reply_markup=default_markup(user=user),parse_mode="HTML"
|
2102
2105
|
) # \n\nThis bot restarts every hour starting at 5:30am IST until 10:30pm IST to keep it running on free servers. If it does not respond, please try again in a minutes to avoid the restart duration!
|
2103
2106
|
query = update.message
|
@@ -426,6 +426,7 @@ def setupLogger(shouldLog=False, trace=False):
|
|
426
426
|
OutputControls().printOutput(colorText.GREEN + f" [+] {log_file_path}"+colorText.END)
|
427
427
|
OutputControls().printOutput(colorText.FAIL + " [+] If you need to share, open this folder, copy and zip the log file to share.\n" + colorText.END)
|
428
428
|
# logger = multiprocessing.log_to_stderr(log.logging.DEBUG)
|
429
|
+
os.environ["PKDevTools_Default_Log_Level"] = str(log.logging.DEBUG)
|
429
430
|
log.setup_custom_logger(
|
430
431
|
"pkscreener",
|
431
432
|
log.logging.DEBUG,
|
@@ -433,31 +434,30 @@ def setupLogger(shouldLog=False, trace=False):
|
|
433
434
|
log_file_path=log_file_path,
|
434
435
|
filter=None,
|
435
436
|
)
|
436
|
-
os.environ["PKDevTools_Default_Log_Level"] = str(log.logging.DEBUG)
|
437
437
|
|
438
438
|
def warnAboutDependencies():
|
439
439
|
if not Imports["talib"]:
|
440
440
|
OutputControls().printOutput(
|
441
441
|
colorText.FAIL
|
442
|
-
+ " [+] TA-Lib is not installed. Looking for
|
442
|
+
+ " [+] TA-Lib is not installed. Looking for pandas_ta_classic."
|
443
443
|
+ colorText.END
|
444
444
|
)
|
445
445
|
sleep(1)
|
446
446
|
issueLink = "https://github.com/pkjmesra/PKScreener"
|
447
447
|
issueLink = f"\x1b[97m\x1b]8;;{issueLink}\x1b\\{issueLink}\x1b]8;;\x1b\\\x1b[0m"
|
448
|
-
if Imports["
|
448
|
+
if Imports["pandas_ta_classic"]:
|
449
449
|
taLink = "https://github.com/ta-lib/ta-lib-python"
|
450
450
|
taLink = f"\x1b[97m\x1b]8;;{taLink}\x1b\\{taLink}\x1b]8;;\x1b\\\x1b[0m"
|
451
451
|
OutputControls().printOutput(
|
452
452
|
colorText.GREEN
|
453
|
-
+ f" [+] Found and falling back on
|
453
|
+
+ f" [+] Found and falling back on pandas_ta_classic.\n [+] For full coverage(candle patterns), you may wish to read the README file in PKScreener repo : {issueLink}\n [+] or follow instructions from\n [+] {taLink}"
|
454
454
|
+ colorText.END
|
455
455
|
)
|
456
456
|
sleep(1)
|
457
457
|
else:
|
458
458
|
OutputControls().printOutput(
|
459
459
|
colorText.FAIL
|
460
|
-
+ f" [+] Neither ta-lib nor
|
460
|
+
+ f" [+] Neither ta-lib nor pandas_ta_classic was located. You need at least one of them to continue! \n [+] Please follow instructions from README file under PKScreener repo: {issueLink}"
|
461
461
|
+ colorText.END
|
462
462
|
)
|
463
463
|
OutputControls().takeUserInput("Press any key to try anyway...")
|
@@ -933,10 +933,10 @@ def pkscreenercli():
|
|
933
933
|
OutputControls().printOutput(e)
|
934
934
|
traceback.print_exc()
|
935
935
|
pass
|
936
|
-
finally:
|
937
|
-
|
938
|
-
|
939
|
-
|
936
|
+
# finally:
|
937
|
+
# from PKDevTools.classes.PKBackupRestore import restore_backup
|
938
|
+
# restore_backup()
|
939
|
+
# sleep(3)
|
940
940
|
# import threading
|
941
941
|
# from pkscreener.globals import tryLoadDataOnBackgroundThread
|
942
942
|
# ping_thread = threading.Thread(target=tryLoadDataOnBackgroundThread, daemon=True)
|