pkscreener 0.46.20250728.754__cp312-cp312-win_amd64.whl → 0.46.20250908.764__cp312-cp312-win_amd64.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.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/LICENSE-Others.txt +1 -1
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/README.txt +6 -6
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/__init__.py +2 -2
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/AssetsManager.py +13 -29
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/Backtest.py +5 -5
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/CandlePatterns.py +23 -23
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/ConfigManager.py +14 -2
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/ConsoleUtility.py +1 -1
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/Fetcher.py +26 -20
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/ImageUtility.py +1 -1
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/MarketMonitor.py +6 -6
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/MarketStatus.py +3 -2
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/MenuOptions.py +4 -4
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/PKMarketOpenCloseAnalyser.py +14 -14
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/PKScanRunner.py +2 -2
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/Pktalib.py +36 -36
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/PortfolioXRay.py +5 -5
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/ScreeningStatistics.py +457 -445
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/StockScreener.py +47 -34
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/Utility.py +4 -3
- pkscreener-0.46.20250908.764.data/purelib/pkscreener/classes/__init__.py +1 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/globals.py +26 -24
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/pkscreenerbot.py +1 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/pkscreenercli.py +9 -9
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/requirements.txt +3 -3
- {pkscreener-0.46.20250728.754.dist-info → pkscreener-0.46.20250908.764.dist-info}/METADATA +8 -8
- pkscreener-0.46.20250908.764.dist-info/RECORD +58 -0
- pkscreener-0.46.20250728.754.data/purelib/pkscreener/classes/__init__.py +0 -1
- pkscreener-0.46.20250728.754.dist-info/RECORD +0 -58
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/Disclaimer.txt +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/LICENSE.txt +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/LogoWM.txt +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/ArtTexts.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/Barometer.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/BaseScreeningStatistics.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/Changelog.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/ConsoleMenuUtility.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/GlobalStore.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/Messenger.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/OtaUpdater.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/PKAnalytics.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/PKDataService.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/PKDemoHandler.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/PKPremiumHandler.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/PKScheduledTaskProgress.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/PKScheduler.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/PKSpreadsheets.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/PKTask.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/PKUserRegistration.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/Portfolio.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/StockSentiment.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/UserMenuChoicesHandler.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/WorkflowManager.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/classes/keys.py +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/courbd.ttf +0 -0
- {pkscreener-0.46.20250728.754.data → pkscreener-0.46.20250908.764.data}/purelib/pkscreener/pkscreener.ini +0 -0
- {pkscreener-0.46.20250728.754.dist-info → pkscreener-0.46.20250908.764.dist-info}/WHEEL +0 -0
- {pkscreener-0.46.20250728.754.dist-info → pkscreener-0.46.20250908.764.dist-info}/entry_points.txt +0 -0
- {pkscreener-0.46.20250728.754.dist-info → pkscreener-0.46.20250908.764.dist-info}/licenses/LICENSE +0 -0
- {pkscreener-0.46.20250728.754.dist-info → pkscreener-0.46.20250908.764.dist-info}/top_level.txt +0 -0
@@ -38,7 +38,7 @@ import pkscreener.classes.Utility as Utility
|
|
38
38
|
from pkscreener import Imports
|
39
39
|
from pkscreener.classes.Pktalib import pktalib
|
40
40
|
from PKDevTools.classes.OutputControls import OutputControls
|
41
|
-
from PKDevTools.classes import Archiver
|
41
|
+
from PKDevTools.classes import Archiver, log
|
42
42
|
from PKNSETools.morningstartools import Stock
|
43
43
|
|
44
44
|
if sys.version_info >= (3, 11):
|
@@ -90,13 +90,25 @@ class ScreeningStatistics:
|
|
90
90
|
self.configManager = configManager
|
91
91
|
self.default_logger = default_logger
|
92
92
|
self.shouldLog = shouldLog
|
93
|
+
self.setupLogger(self.default_logger.level)
|
94
|
+
|
95
|
+
def setupLogger(self, log_level):
|
96
|
+
if log_level > 0:
|
97
|
+
os.environ["PKDevTools_Default_Log_Level"] = str(log_level)
|
98
|
+
log.setup_custom_logger(
|
99
|
+
"pkscreener",
|
100
|
+
log_level,
|
101
|
+
trace=False,
|
102
|
+
log_file_path="pkscreener-logs.txt",
|
103
|
+
filter=None,
|
104
|
+
)
|
93
105
|
|
94
106
|
def calc_relative_strength(self,df:pd.DataFrame):
|
95
107
|
if df is None or len(df) <= 1:
|
96
108
|
return -1
|
97
109
|
closeColumn = 'Adj Close'
|
98
110
|
if closeColumn not in df.columns:
|
99
|
-
closeColumn =
|
111
|
+
closeColumn = "close"
|
100
112
|
|
101
113
|
with pd.option_context('mode.chained_assignment', None):
|
102
114
|
df.sort_index(inplace=True)
|
@@ -119,13 +131,13 @@ class ScreeningStatistics:
|
|
119
131
|
if Imports["vectorbt"]:
|
120
132
|
from vectorbt.indicators import MA as vbt
|
121
133
|
if df is not None:
|
122
|
-
ema = vbt.run(df["
|
134
|
+
ema = vbt.run(df["close"], 1, short_name='EMA', ewm=True)
|
123
135
|
df["Above"] = ema.ma_crossed_above(df["ATRTrailingStop"])
|
124
136
|
df["Below"] = ema.ma_crossed_below(df["ATRTrailingStop"])
|
125
137
|
else:
|
126
138
|
OutputControls().printOutput(f"{colorText.FAIL}The main module needed for best Buy/Sell result calculation is missing. Falling back on an alternative, but it is not very reliable.{colorText.END}")
|
127
139
|
if df is not None:
|
128
|
-
ema = pktalib.EMA(df["
|
140
|
+
ema = pktalib.EMA(df["close"], ema_period) if ema_period > 1 else df["close"]#short_name='EMA', ewm=True)
|
129
141
|
df["Above"] = ema > df["ATRTrailingStop"]
|
130
142
|
df["Below"] = ema < df["ATRTrailingStop"]
|
131
143
|
except (OSError,FileNotFoundError) as e: # pragma: no cover
|
@@ -160,7 +172,7 @@ class ScreeningStatistics:
|
|
160
172
|
else:
|
161
173
|
OutputControls().printOutput(msg)
|
162
174
|
if df is not None:
|
163
|
-
ema = pktalib.EMA(df["
|
175
|
+
ema = pktalib.EMA(df["close"], ema_period) if ema_period > 1 else df["close"]#short_name='EMA', ewm=True)
|
164
176
|
df["Above"] = ema > df["ATRTrailingStop"]
|
165
177
|
df["Below"] = ema < df["ATRTrailingStop"]
|
166
178
|
except KeyboardInterrupt: # pragma: no cover
|
@@ -169,8 +181,8 @@ class ScreeningStatistics:
|
|
169
181
|
pass
|
170
182
|
|
171
183
|
if df is not None:
|
172
|
-
df["Buy"] = (df["
|
173
|
-
df["Sell"] = (df["
|
184
|
+
df["Buy"] = (df["close"] > df["ATRTrailingStop"]) & (df["Above"]==True)
|
185
|
+
df["Sell"] = (df["close"] < df["ATRTrailingStop"]) & (df["Below"]==True)
|
174
186
|
|
175
187
|
return df
|
176
188
|
|
@@ -179,8 +191,8 @@ class ScreeningStatistics:
|
|
179
191
|
dataframe = self.findBuySellSignalsFromATRTrailing(dataframe, key_value=2, atr_period=7, ema_period=100)
|
180
192
|
|
181
193
|
# Calculate RSI and ADX
|
182
|
-
rsi = pktalib.RSI(dataframe[
|
183
|
-
adx = pktalib.ADX(dataframe[
|
194
|
+
rsi = pktalib.RSI(dataframe["close"])
|
195
|
+
adx = pktalib.ADX(dataframe["high"], dataframe["low"], dataframe["close"])
|
184
196
|
|
185
197
|
# Define conditions based on UTBot Alerts and additional indicators
|
186
198
|
# ... (your custom conditions here)
|
@@ -233,9 +245,9 @@ class ScreeningStatistics:
|
|
233
245
|
data = data.fillna(0)
|
234
246
|
data = data.replace([np.inf, -np.inf], 0)
|
235
247
|
one_week = 5
|
236
|
-
recent = data.head(1)["
|
248
|
+
recent = data.head(1)["high"].iloc[0]
|
237
249
|
full52Week = data.head(50 * one_week)
|
238
|
-
full52WeekHigh = full52Week["
|
250
|
+
full52WeekHigh = full52Week["high"].max()
|
239
251
|
# if self.shouldLog:
|
240
252
|
# self.default_logger.debug(data.head(10))
|
241
253
|
return recent >= full52WeekHigh
|
@@ -251,10 +263,10 @@ class ScreeningStatistics:
|
|
251
263
|
one_week = 5
|
252
264
|
week_52 = one_week * 50 # Considering holidays etc as well of 10 days
|
253
265
|
full52Week = data.head(week_52 + 1).tail(week_52+1)
|
254
|
-
recentHigh = data.head(1)["
|
255
|
-
recentLow = data.head(1)["
|
256
|
-
full52WeekHigh = full52Week["
|
257
|
-
full52WeekLow = full52Week["
|
266
|
+
recentHigh = data.head(1)["high"].iloc[0]
|
267
|
+
recentLow = data.head(1)["low"].iloc[0]
|
268
|
+
full52WeekHigh = full52Week["high"].max()
|
269
|
+
full52WeekLow = full52Week["low"].min()
|
258
270
|
|
259
271
|
saveDict["52Wk-H"] = "{:.2f}".format(full52WeekHigh)
|
260
272
|
saveDict["52Wk-L"] = "{:.2f}".format(full52WeekLow)
|
@@ -287,12 +299,12 @@ class ScreeningStatistics:
|
|
287
299
|
data = data.fillna(0)
|
288
300
|
data = data.replace([np.inf, -np.inf], 0)
|
289
301
|
one_week = 5
|
290
|
-
recent = data.head(1)["
|
302
|
+
recent = data.head(1)["low"].iloc[0]
|
291
303
|
last1Week = data.head(one_week)
|
292
304
|
last2Week = data.head(2 * one_week)
|
293
305
|
previousWeek = last2Week.tail(one_week)
|
294
|
-
last1WeekLow = last1Week["
|
295
|
-
previousWeekLow = previousWeek["
|
306
|
+
last1WeekLow = last1Week["low"].min()
|
307
|
+
previousWeekLow = previousWeek["low"].min()
|
296
308
|
# if self.shouldLog:
|
297
309
|
# self.default_logger.debug(data.head(10))
|
298
310
|
return (recent <= min(previousWeekLow, last1WeekLow)) and (
|
@@ -308,14 +320,14 @@ class ScreeningStatistics:
|
|
308
320
|
data = data.fillna(0)
|
309
321
|
data = data.replace([np.inf, -np.inf], 0)
|
310
322
|
one_week = 5
|
311
|
-
recent = data.head(1)["
|
323
|
+
recent = data.head(1)["low"].iloc[0]
|
312
324
|
# last1Week = data.head(one_week)
|
313
325
|
# last2Week = data.head(2 * one_week)
|
314
326
|
# previousWeek = last2Week.tail(one_week)
|
315
327
|
full52Week = data.head(50 * one_week)
|
316
|
-
# last1WeekLow = last1Week["
|
317
|
-
# previousWeekLow = previousWeek["
|
318
|
-
full52WeekLow = full52Week["
|
328
|
+
# last1WeekLow = last1Week["low"].min()
|
329
|
+
# previousWeekLow = previousWeek["low"].min()
|
330
|
+
full52WeekLow = full52Week["low"].min()
|
319
331
|
# if self.shouldLog:
|
320
332
|
# self.default_logger.debug(data.head(10))
|
321
333
|
return recent <= full52WeekLow
|
@@ -329,7 +341,7 @@ class ScreeningStatistics:
|
|
329
341
|
data = data.replace([np.inf, -np.inf], 0)
|
330
342
|
period = 14
|
331
343
|
data = data[::-1] # Reverse the dataframe so that its the oldest date first
|
332
|
-
aroondf = pktalib.Aroon(data["
|
344
|
+
aroondf = pktalib.Aroon(data["high"], data["low"], period)
|
333
345
|
recent = aroondf.tail(1)
|
334
346
|
up = recent[f"AROONU_{period}"].iloc[0]
|
335
347
|
down = recent[f"AROOND_{period}"].iloc[0]
|
@@ -348,11 +360,11 @@ class ScreeningStatistics:
|
|
348
360
|
recent = data.head(1)
|
349
361
|
recentCandleHeight = self.getCandleBodyHeight(recent)
|
350
362
|
data = data[::-1] # Reverse the dataframe so that its the oldest date first
|
351
|
-
atr = pktalib.ATR(data["
|
363
|
+
atr = pktalib.ATR(data["high"],data["low"],data["close"], 14)
|
352
364
|
atrCross = recentCandleHeight >= atr.tail(1).iloc[0]
|
353
365
|
bullishRSI = recent["RSI"].iloc[0] >= 55 or recent["RSIi"].iloc[0] >= 55
|
354
|
-
smav7 = pktalib.SMA(data["
|
355
|
-
atrCrossCondition = atrCross and bullishRSI and (smav7 < recent["
|
366
|
+
smav7 = pktalib.SMA(data["volume"],timeperiod=7).tail(1).iloc[0]
|
367
|
+
atrCrossCondition = atrCross and bullishRSI and (smav7 < recent["volume"].iloc[0])
|
356
368
|
saveDict["ATR"] = round(atr.tail(1).iloc[0],1)
|
357
369
|
screenDict["ATR"] = saveDict["ATR"] #(colorText.GREEN if atrCrossCondition else colorText.FAIL) + str(atr.tail(1).iloc[0]) + colorText.END
|
358
370
|
# if self.shouldLog:
|
@@ -369,7 +381,7 @@ class ScreeningStatistics:
|
|
369
381
|
|
370
382
|
SENSITIVITY = sensitivity
|
371
383
|
# Compute ATR And nLoss variable
|
372
|
-
data["xATR"] = pktalib.ATR(data["
|
384
|
+
data["xATR"] = pktalib.ATR(data["high"], data["low"], data["close"], timeperiod=atr_period)
|
373
385
|
data["nLoss"] = SENSITIVITY * data["xATR"]
|
374
386
|
|
375
387
|
#Drop all rows that have nan, X first depending on the ATR preiod for the moving average
|
@@ -380,8 +392,8 @@ class ScreeningStatistics:
|
|
380
392
|
|
381
393
|
for i in range(1, len(data)):
|
382
394
|
data.loc[i, "ATRTrailingStop"] = self.xATRTrailingStop_func(
|
383
|
-
data.loc[i, "
|
384
|
-
data.loc[i - 1, "
|
395
|
+
data.loc[i, "close"],
|
396
|
+
data.loc[i - 1, "close"],
|
385
397
|
data.loc[i - 1, "ATRTrailingStop"],
|
386
398
|
data.loc[i, "nLoss"],
|
387
399
|
)
|
@@ -416,11 +428,11 @@ class ScreeningStatistics:
|
|
416
428
|
|
417
429
|
# if base_count <= 4: # Maximum of 4 base candles for weekly or monthly timeframe, else 3 for daily
|
418
430
|
# if j < len(data) and data['Candle Type'][j] == 'Rally Candle':
|
419
|
-
# if data[
|
431
|
+
# if data["close"][j] > data["low"][i] + 0.6 * data['Candle Range'][i] and data["high"][i] <= cmp:
|
420
432
|
# # Check for one more rally candle or green base candle
|
421
433
|
# k = j + 1
|
422
434
|
# while k < len(data):
|
423
|
-
# if data['Candle Type'][k] == 'Rally Candle' or (data['Candle Type'][k] == 'Base Candle' and data[
|
435
|
+
# if data['Candle Type'][k] == 'Rally Candle' or (data['Candle Type'][k] == 'Base Candle' and data["close"][k] > data["open"][k]):
|
424
436
|
# demand_zones.append((i, j, 'Drop Base Rally', base_count))
|
425
437
|
# drop_base_rally_zone = True
|
426
438
|
# break
|
@@ -434,11 +446,11 @@ class ScreeningStatistics:
|
|
434
446
|
|
435
447
|
# if base_count >= 1: # At least one base candle required
|
436
448
|
# if j < len(data) and data['Candle Type'][j] == 'Rally Candle':
|
437
|
-
# if data[
|
449
|
+
# if data["close"][j] > data["close"][i] and data["high"][i] <= cmp: # New condition: close of 2nd rally candle > 1st rally candle
|
438
450
|
# # Check for one more rally candle or green base candle
|
439
451
|
# k = j + 1
|
440
452
|
# while k < len(data):
|
441
|
-
# if data['Candle Type'][k] == 'Rally Candle' or (data['Candle Type'][k] == 'Base Candle' and data[
|
453
|
+
# if data['Candle Type'][k] == 'Rally Candle' or (data['Candle Type'][k] == 'Base Candle' and data["close"][k] > data["open"][k]):
|
442
454
|
# demand_zones.append((i, j, 'Rally Base Rally', base_count))
|
443
455
|
# rally_base_rally_zone = True
|
444
456
|
# break
|
@@ -446,7 +458,7 @@ class ScreeningStatistics:
|
|
446
458
|
|
447
459
|
# # Collect base candle prices for proximal line calculation
|
448
460
|
# if data['Candle Type'][i] == 'Base Candle':
|
449
|
-
# base_candle_prices.append(data[
|
461
|
+
# base_candle_prices.append(data["close"][i])
|
450
462
|
|
451
463
|
# # Calculate proximal line price (highest price among base candles)
|
452
464
|
# proximal_line_price = max(base_candle_prices) if base_candle_prices else None
|
@@ -471,12 +483,12 @@ class ScreeningStatistics:
|
|
471
483
|
|
472
484
|
# if base_count <= 4: # Maximum of 4 base candles for weekly or monthly timeframe, else 3 for daily
|
473
485
|
# if j < len(data) and data['Candle Type'][j] == 'Drop Candle':
|
474
|
-
# if data[
|
486
|
+
# if data["close"][i] < data["low"][j] and data["low"][i] >= cmp: # New condition: close of drop candle < low of base candle
|
475
487
|
# # New logic: Look for one more drop candle or red base candle
|
476
488
|
# k = j + 1
|
477
|
-
# while k < len(data) and (data['Candle Type'][k] == 'Drop Candle' or data[
|
489
|
+
# while k < len(data) and (data['Candle Type'][k] == 'Drop Candle' or data["close"][k] < data["open"][k]):
|
478
490
|
# k += 1
|
479
|
-
# if k < len(data) and (data['Candle Type'][k] == 'Drop Candle' or data[
|
491
|
+
# if k < len(data) and (data['Candle Type'][k] == 'Drop Candle' or data["close"][k] < data["open"][k]):
|
480
492
|
# supply_zones.append((i, j, 'Drop Base Drop', base_count))
|
481
493
|
# drop_base_drop_zone = True
|
482
494
|
# elif data['Candle Type'][i] == 'Rally Candle' and data['Candle Type'][i + 1] == 'Base Candle':
|
@@ -488,13 +500,13 @@ class ScreeningStatistics:
|
|
488
500
|
|
489
501
|
# if base_count >= 1: # At least one base candle required
|
490
502
|
# if j < len(data) and data['Candle Type'][j] == 'Drop Candle':
|
491
|
-
# if data[
|
503
|
+
# if data["close"][j] < data["open"][j] and data["low"][i] >= cmp: # Modified condition: close of drop candle < open of drop candle
|
492
504
|
# supply_zones.append((i, j, 'Rally Base Drop', base_count))
|
493
505
|
# rally_base_drop_zone = True
|
494
506
|
|
495
507
|
# # Collect base candle prices for proximal line calculation
|
496
508
|
# if data['Candle Type'][i] == 'Base Candle':
|
497
|
-
# base_candle_prices.append(data[
|
509
|
+
# base_candle_prices.append(data["close"][i])
|
498
510
|
|
499
511
|
# # Calculate proximal line price (lowest price among base candles)
|
500
512
|
# proximal_line_price = min(base_candle_prices) if base_candle_prices else None
|
@@ -504,7 +516,7 @@ class ScreeningStatistics:
|
|
504
516
|
# def calculate_demand_proximal_lines(self,data, demand_zones):
|
505
517
|
# proximal_line_prices = []
|
506
518
|
# for start, end, _, _ in demand_zones:
|
507
|
-
# base_candle_prices = data.loc[(data['Candle Type'] == 'Base Candle') & (data.index >= data.index[start]) & (data.index <= data.index[end]), [
|
519
|
+
# base_candle_prices = data.loc[(data['Candle Type'] == 'Base Candle') & (data.index >= data.index[start]) & (data.index <= data.index[end]), ["open", "close"]]
|
508
520
|
# max_price = base_candle_prices.max(axis=1).max() # Get the maximum price among all base candles' open and close prices
|
509
521
|
# proximal_line_prices.append(max_price)
|
510
522
|
# return proximal_line_prices
|
@@ -512,7 +524,7 @@ class ScreeningStatistics:
|
|
512
524
|
# def calculate_supply_proximal_lines(self,data, supply_zones):
|
513
525
|
# proximal_line_prices = []
|
514
526
|
# for start, end, _, _ in supply_zones:
|
515
|
-
# base_candle_prices = data.loc[(data['Candle Type'] == 'Base Candle') & (data.index >= data.index[start]) & (data.index <= data.index[end]), [
|
527
|
+
# base_candle_prices = data.loc[(data['Candle Type'] == 'Base Candle') & (data.index >= data.index[start]) & (data.index <= data.index[end]), ["open", "close"]]
|
516
528
|
# min_price = base_candle_prices.min(axis=1).min() # Get the minimum price among all base candles' open and close prices
|
517
529
|
# proximal_line_prices.append(min_price)
|
518
530
|
# return proximal_line_prices
|
@@ -522,12 +534,12 @@ class ScreeningStatistics:
|
|
522
534
|
# for start, end, pattern, _ in demand_zones:
|
523
535
|
# if pattern == 'Drop Base Rally':
|
524
536
|
# # Logic for Drop Base Rally pattern: Take the lowest price among all components of the zone
|
525
|
-
# lowest_price = min(data[
|
537
|
+
# lowest_price = min(data["low"][start:end + 1]) # Get the lowest price within the zone
|
526
538
|
# distal_line_prices.append(lowest_price)
|
527
539
|
# elif pattern == 'Rally Base Rally':
|
528
540
|
# # Logic for Rally Base Rally pattern: Take the lowest of only all base candle and followed rally candle
|
529
|
-
# base_candle_prices = data.loc[(data['Candle Type'] == 'Base Candle') & (data.index >= data.index[start]) & (data.index <= data.index[end]),
|
530
|
-
# rally_candle_prices = data.loc[(data['Candle Type'] == 'Rally Candle') & (data.index >= data.index[end]) & (data.index < data.index[end+1]),
|
541
|
+
# base_candle_prices = data.loc[(data['Candle Type'] == 'Base Candle') & (data.index >= data.index[start]) & (data.index <= data.index[end]), "low"]
|
542
|
+
# rally_candle_prices = data.loc[(data['Candle Type'] == 'Rally Candle') & (data.index >= data.index[end]) & (data.index < data.index[end+1]), "low"]
|
531
543
|
# all_prices = pd.concat([base_candle_prices, rally_candle_prices])
|
532
544
|
# lowest_price = all_prices.min() if not all_prices.empty else None
|
533
545
|
# distal_line_prices.append(lowest_price)
|
@@ -538,12 +550,12 @@ class ScreeningStatistics:
|
|
538
550
|
# for start, end, pattern, _ in supply_zones:
|
539
551
|
# if pattern == 'Rally Base Drop':
|
540
552
|
# # Logic for Rally Base Drop pattern: Take the highest price among all components of the zone
|
541
|
-
# highest_price = max(data[
|
553
|
+
# highest_price = max(data["high"][start:end + 1]) # Get the highest price within the zone
|
542
554
|
# distal_line_prices.append(highest_price)
|
543
555
|
# elif pattern == 'Drop Base Drop':
|
544
556
|
# # Logic for Drop Base Drop pattern: Take the highest of only all base candles and followed drop candle
|
545
|
-
# base_candle_prices = data.loc[(data['Candle Type'] == 'Base Candle') & (data.index >= data.index[start]) & (data.index <= data.index[end]),
|
546
|
-
# drop_candle_prices = data.loc[(data['Candle Type'] == 'Drop Candle') & (data.index >= data.index[start]) & (data.index <= data.index[end]),
|
557
|
+
# base_candle_prices = data.loc[(data['Candle Type'] == 'Base Candle') & (data.index >= data.index[start]) & (data.index <= data.index[end]), "high"]
|
558
|
+
# drop_candle_prices = data.loc[(data['Candle Type'] == 'Drop Candle') & (data.index >= data.index[start]) & (data.index <= data.index[end]), "high"]
|
547
559
|
# all_prices = pd.concat([base_candle_prices, drop_candle_prices])
|
548
560
|
# highest_price = all_prices.max() if not all_prices.empty else None
|
549
561
|
# distal_line_prices.append(highest_price)
|
@@ -563,7 +575,7 @@ class ScreeningStatistics:
|
|
563
575
|
# - True if the proximal line price is tested, False otherwise
|
564
576
|
# """
|
565
577
|
# for i in range(end_index + 1, len(data)):
|
566
|
-
# if data[
|
578
|
+
# if data["low"][i] <= proximal_line_price <= data["high"][i]:
|
567
579
|
# return True
|
568
580
|
# return False
|
569
581
|
|
@@ -623,7 +635,7 @@ class ScreeningStatistics:
|
|
623
635
|
# filtered_stocks = []
|
624
636
|
# for symbol in symbol_list:
|
625
637
|
# if data is not None:
|
626
|
-
# cmp = data.iloc[-1][
|
638
|
+
# cmp = data.iloc[-1]["close"] # Current market price
|
627
639
|
# demand_zones, _, _, demand_proximal_line = self.identify_demand_zone(data, cmp) # Pass cmp argument here
|
628
640
|
# supply_zones, _, _, supply_proximal_line = self.identify_supply_zone(data, cmp) # Pass cmp argument here
|
629
641
|
|
@@ -641,7 +653,7 @@ class ScreeningStatistics:
|
|
641
653
|
# with open("demand_supply_zones.txt", "w") as file:
|
642
654
|
# for symbol in data["Stock"]:
|
643
655
|
# if data is not None:
|
644
|
-
# cmp = data.iloc[-1][
|
656
|
+
# cmp = data.iloc[-1]["close"] # Current market price
|
645
657
|
# demand_zones, _, _, demand_proximal_line = self.identify_demand_zone(data, cmp)
|
646
658
|
# supply_zones, _, _, supply_proximal_line = self.identify_supply_zone(data, cmp)
|
647
659
|
|
@@ -724,7 +736,7 @@ class ScreeningStatistics:
|
|
724
736
|
|
725
737
|
# for symbol in filtered_stocks:
|
726
738
|
# if data is not None:
|
727
|
-
# cmp = data.iloc[-1][
|
739
|
+
# cmp = data.iloc[-1]["close"] # Current market price
|
728
740
|
# demand_zones, _, _, demand_proximal_line = self.identify_demand_zone(data, cmp)
|
729
741
|
# supply_zones, _, _, supply_proximal_line = self.identify_supply_zone(data, cmp)
|
730
742
|
|
@@ -830,9 +842,9 @@ class ScreeningStatistics:
|
|
830
842
|
latestRecordsFirst_df = latestRecordsFirst_df.fillna(0)
|
831
843
|
latestRecordsFirst_df = latestRecordsFirst_df.replace([np.inf, -np.inf], 0)
|
832
844
|
# Bollinger bands
|
833
|
-
latestRecordsFirst_df.loc[:,'BBands-U'], latestRecordsFirst_df.loc[:,'BBands-M'], latestRecordsFirst_df.loc[:,'BBands-L'] = pktalib.BBANDS(latestRecordsFirst_df["
|
845
|
+
latestRecordsFirst_df.loc[:,'BBands-U'], latestRecordsFirst_df.loc[:,'BBands-M'], latestRecordsFirst_df.loc[:,'BBands-L'] = pktalib.BBANDS(latestRecordsFirst_df["close"], 20)
|
834
846
|
# compute Keltner's channel
|
835
|
-
latestRecordsFirst_df['low_kel'], latestRecordsFirst_df['upp_kel'] = pktalib.KeltnersChannel(latestRecordsFirst_df["
|
847
|
+
latestRecordsFirst_df['low_kel'], latestRecordsFirst_df['upp_kel'] = pktalib.KeltnersChannel(latestRecordsFirst_df["high"], latestRecordsFirst_df["low"],latestRecordsFirst_df["close"],20)
|
836
848
|
# squeeze indicator
|
837
849
|
def in_squeeze(df):
|
838
850
|
return df['low_kel'] < df['BBands-L'] < df['BBands-U'] < df['upp_kel']
|
@@ -851,8 +863,8 @@ class ScreeningStatistics:
|
|
851
863
|
if filter not in [1,3,4]: # Buy/Sell/All
|
852
864
|
return False
|
853
865
|
# decide which action to take by comparing distances
|
854
|
-
distance_to_upper = abs(latestRecordsFirst_df['BBands-U'].values[-1] - latestRecordsFirst_df[
|
855
|
-
distance_to_lower = abs(latestRecordsFirst_df['BBands-L'].values[-1] - latestRecordsFirst_df[
|
866
|
+
distance_to_upper = abs(latestRecordsFirst_df['BBands-U'].values[-1] - latestRecordsFirst_df["close"].values[-1])
|
867
|
+
distance_to_lower = abs(latestRecordsFirst_df['BBands-L'].values[-1] - latestRecordsFirst_df["close"].values[-1])
|
856
868
|
|
857
869
|
action = False
|
858
870
|
if distance_to_upper < distance_to_lower:
|
@@ -892,13 +904,13 @@ class ScreeningStatistics:
|
|
892
904
|
candleHeight = abs(self.getCandleBodyHeight(data[candle:]))
|
893
905
|
totalCandleHeight += candleHeight
|
894
906
|
|
895
|
-
reversedData.loc[:,'BBands-U'], reversedData.loc[:,'BBands-M'], reversedData.loc[:,'BBands-L'] = pktalib.BBANDS(reversedData["
|
907
|
+
reversedData.loc[:,'BBands-U'], reversedData.loc[:,'BBands-M'], reversedData.loc[:,'BBands-L'] = pktalib.BBANDS(reversedData["close"], 20)
|
896
908
|
reversedData = reversedData[::-1]
|
897
909
|
recents = reversedData.head(6)
|
898
910
|
ulr = self.non_zero_range(recents.loc[:,'BBands-U'], recents.loc[:,'BBands-L'])
|
899
911
|
maxOfLast5Candles = ulr.tail(5).max()
|
900
912
|
# bandwidth = 100 * ulr / recents.loc[:,'BBands-M']
|
901
|
-
# percent = self.non_zero_range(recents.loc[:,
|
913
|
+
# percent = self.non_zero_range(recents.loc[:,"close"], recents.loc[:,'BBands-L']) / ulr
|
902
914
|
saveDict["bbands_ulr_ratio_max5"] = round(ulr.iloc[0]/maxOfLast5Candles,2) #percent.iloc[0]
|
903
915
|
screenDict["bbands_ulr_ratio_max5"] = saveDict["bbands_ulr_ratio_max5"]
|
904
916
|
# saveDict["bbands_bandwidth"] = bandwidth.iloc[0]
|
@@ -924,9 +936,9 @@ class ScreeningStatistics:
|
|
924
936
|
data = data.replace([np.inf, -np.inf], 0)
|
925
937
|
recent = data.head(1)
|
926
938
|
data = data[1:]
|
927
|
-
maxHigh = round(data.describe()["
|
928
|
-
maxClose = round(data.describe()["
|
929
|
-
recentClose = round(recent["
|
939
|
+
maxHigh = round(data.describe()["high"]["max"], 2)
|
940
|
+
maxClose = round(data.describe()["close"]["max"], 2)
|
941
|
+
recentClose = round(recent["close"].iloc[0], 2)
|
930
942
|
if np.isnan(maxClose) or np.isnan(maxHigh):
|
931
943
|
saveDict["Breakout"] = "BO: 0 R: 0"
|
932
944
|
screenDict["Breakout"] = (
|
@@ -968,7 +980,7 @@ class ScreeningStatistics:
|
|
968
980
|
+ colorText.END
|
969
981
|
)
|
970
982
|
return not alreadyBrokenout
|
971
|
-
noOfHigherShadows = len(data[data.
|
983
|
+
noOfHigherShadows = len(data[data.high > maxClose])
|
972
984
|
if daysToLookback / noOfHigherShadows <= 3:
|
973
985
|
saveDict["Breakout"] = "BO: " + str(maxHigh) + " R: 0"
|
974
986
|
if recentClose >= maxHigh:
|
@@ -1058,18 +1070,18 @@ class ScreeningStatistics:
|
|
1058
1070
|
data = data.replace([np.inf, -np.inf], 0)
|
1059
1071
|
reversedData = data[::-1] # Reverse the dataframe so that its the oldest date first
|
1060
1072
|
# Find the anchor point. Find the candle where there's a major dip.
|
1061
|
-
majorLow = reversedData["
|
1062
|
-
lowRow = reversedData[reversedData["
|
1073
|
+
majorLow = reversedData["low"].min()
|
1074
|
+
lowRow = reversedData[reversedData["low"] == majorLow]
|
1063
1075
|
anchored_date = lowRow.index[0]
|
1064
1076
|
avwap = pktalib.AVWAP(df=reversedData,anchored_date=anchored_date)
|
1065
1077
|
if 'anchored_VWAP' not in reversedData.keys():
|
1066
1078
|
reversedData.loc[:,'anchored_VWAP'] =avwap
|
1067
|
-
recentOpen = reversedData["
|
1068
|
-
recentClose = reversedData["
|
1069
|
-
recentLow = reversedData["
|
1079
|
+
recentOpen = reversedData["open"].tail(1).head(1).iloc[0]
|
1080
|
+
recentClose = reversedData["close"].tail(1).head(1).iloc[0]
|
1081
|
+
recentLow = reversedData["low"].tail(1).head(1).iloc[0]
|
1070
1082
|
recentAVWAP = reversedData["anchored_VWAP"].tail(1).head(1).iloc[0]
|
1071
|
-
recentVol = reversedData["
|
1072
|
-
prevVol = reversedData["
|
1083
|
+
recentVol = reversedData["volume"].tail(1).head(1).iloc[0]
|
1084
|
+
prevVol = reversedData["volume"].tail(2).head(1).iloc[0]
|
1073
1085
|
avwap.replace(np.inf, np.nan).replace(-np.inf, np.nan).dropna(inplace=True)
|
1074
1086
|
reversedData = reversedData.tail(len(avwap))
|
1075
1087
|
diffFromAVWAP = (abs(recentClose-recentAVWAP)/recentAVWAP) * 100
|
@@ -1104,15 +1116,15 @@ class ScreeningStatistics:
|
|
1104
1116
|
data = data.fillna(0)
|
1105
1117
|
data = data.replace([np.inf, -np.inf], 0)
|
1106
1118
|
data = data[::-1] # Reverse the dataframe so that its the oldest date first
|
1107
|
-
data["RSI12"] = pktalib.RSI(data["
|
1108
|
-
data["EMA10"] = pktalib.EMA(data["
|
1109
|
-
data["EMA200"] = pktalib.EMA(data["
|
1110
|
-
macd = pktalib.MACD(data["
|
1119
|
+
data["RSI12"] = pktalib.RSI(data["close"], 12)
|
1120
|
+
data["EMA10"] = pktalib.EMA(data["close"], 10)
|
1121
|
+
data["EMA200"] = pktalib.EMA(data["close"], 200)
|
1122
|
+
macd = pktalib.MACD(data["close"], 10, 18, 9)[2].tail(1)
|
1111
1123
|
recent = data.tail(1)
|
1112
1124
|
cond1 = recent["RSI12"].iloc[0] > 55
|
1113
1125
|
cond2 = cond1 and (macd.iloc[:1][0] > 0)
|
1114
|
-
cond3 = cond2 and (recent["
|
1115
|
-
cond4 = cond3 and (recent["
|
1126
|
+
cond3 = cond2 and (recent["close"].iloc[0] > recent["EMA10"].iloc[0])
|
1127
|
+
cond4 = cond3 and (recent["close"].iloc[0] > recent["EMA200"].iloc[0])
|
1116
1128
|
return cond4
|
1117
1129
|
|
1118
1130
|
def findBuySellSignalsFromATRTrailing(self,df, key_value=1, atr_period=10, ema_period=200,buySellAll=1,saveDict=None,screenDict=None):
|
@@ -1124,9 +1136,9 @@ class ScreeningStatistics:
|
|
1124
1136
|
data = data[::-1] # Reverse the dataframe so that its the oldest date first
|
1125
1137
|
|
1126
1138
|
# Calculate ATR and xATRTrailingStop
|
1127
|
-
xATR = np.array(pktalib.ATR(data[
|
1139
|
+
xATR = np.array(pktalib.ATR(data["high"], data["low"], data["close"], timeperiod=atr_period))
|
1128
1140
|
nLoss = key_value * xATR
|
1129
|
-
src = data[
|
1141
|
+
src = data["close"]
|
1130
1142
|
# Initialize arrays
|
1131
1143
|
xATRTrailingStop = np.zeros(len(data))
|
1132
1144
|
xATRTrailingStop[0] = src[0] - nLoss[0]
|
@@ -1148,7 +1160,7 @@ class ScreeningStatistics:
|
|
1148
1160
|
pos = np.where(mask_sell, -1, pos)
|
1149
1161
|
pos[~((pos == 1) | (pos == -1))] = 0
|
1150
1162
|
|
1151
|
-
ema = np.array(pktalib.EMA(data[
|
1163
|
+
ema = np.array(pktalib.EMA(data["close"], timeperiod=ema_period))
|
1152
1164
|
|
1153
1165
|
buy_condition_utbot = (xATRTrailingStop > ema) & (pos > 0) & (src > ema)
|
1154
1166
|
sell_condition_utbot = (xATRTrailingStop < ema) & (pos < 0) & (src < ema)
|
@@ -1214,9 +1226,9 @@ class ScreeningStatistics:
|
|
1214
1226
|
|
1215
1227
|
data = data.reset_index()
|
1216
1228
|
data['Date'] = data['Date'].apply(lambda x : x.strftime('%Y-%m-%d'))
|
1217
|
-
data['Close_ch'] = data[
|
1218
|
-
data['rpv'] = ((data[
|
1219
|
-
data['SMA50_Volume'] = data.
|
1229
|
+
data['Close_ch'] = data["close"].shift(+1)
|
1230
|
+
data['rpv'] = ((data["close"] / data['Close_ch']) - 1) * data["volume"]
|
1231
|
+
data['SMA50_Volume'] = data.volume.rolling(50).mean()
|
1220
1232
|
data['SMA50_rpv'] = data.rpv.rolling(50).mean()
|
1221
1233
|
|
1222
1234
|
T = 0
|
@@ -1226,17 +1238,17 @@ class ScreeningStatistics:
|
|
1226
1238
|
while t < len(data)-T:
|
1227
1239
|
dat = data.loc[t:]
|
1228
1240
|
Dk = dat.loc[t]['Date']
|
1229
|
-
Pk = dat.loc[t][
|
1241
|
+
Pk = dat.loc[t]["close"]
|
1230
1242
|
# search for region K to A
|
1231
1243
|
k = 25
|
1232
1244
|
while k > 15:
|
1233
1245
|
#print('Searching SETUP with width = ', k)
|
1234
1246
|
datA = dat.loc[:t+k] # Dk = t
|
1235
1247
|
# first find absolute maxima point A
|
1236
|
-
Da_index = datA[datA[
|
1237
|
-
Da_value = datA[datA[
|
1238
|
-
Pa_index = datA[datA[
|
1239
|
-
Pa_value = datA[datA[
|
1248
|
+
Da_index = datA[datA["close"] == max(datA["close"])]['Date'].index[0]
|
1249
|
+
Da_value = datA[datA["close"] == max(datA["close"])]['Date'].values[0]
|
1250
|
+
Pa_index = datA[datA["close"] == max(datA["close"])]["close"].index[0]
|
1251
|
+
Pa_value = datA[datA["close"] == max(datA["close"])]["close"].values[0]
|
1240
1252
|
uprv1 = abs(datA.loc[t:Da_index].loc[datA['rpv'] > 0, :]['rpv'].mean())
|
1241
1253
|
dprv1 = abs(datA.loc[t:Da_index].loc[datA['rpv'] <= 0, :]['rpv'].mean())
|
1242
1254
|
if (dprv1 == 'NaN') | (dprv1 == 0):
|
@@ -1250,11 +1262,11 @@ class ScreeningStatistics:
|
|
1250
1262
|
while a > 10:
|
1251
1263
|
#print('Lets search for LEFT SIDE CUP with width = ', a)
|
1252
1264
|
datB = dat.loc[Da_index:Da_index+a]
|
1253
|
-
Db_index = datB[datB[
|
1254
|
-
Db_value = datB[datB[
|
1255
|
-
Pb_index = datB[datB[
|
1256
|
-
Pb_value = datB[datB[
|
1257
|
-
avg_vol = datB[
|
1265
|
+
Db_index = datB[datB["close"] == min(datB["close"])]['Date'].index[0]
|
1266
|
+
Db_value = datB[datB["close"] == min(datB["close"])]['Date'].values[0]
|
1267
|
+
Pb_index = datB[datB["close"] == min(datB["close"])]["close"].index[0]
|
1268
|
+
Pb_value = datB[datB["close"] == min(datB["close"])]["close"].values[0]
|
1269
|
+
avg_vol = datB["volume"].mean()
|
1258
1270
|
avg_ma_vol = data['SMA50_Volume'].mean()
|
1259
1271
|
if (Pb_value < Pa_value) & (avg_vol < avg_ma_vol):
|
1260
1272
|
#print("Voila! You found the bottom, it's all uphill from here")
|
@@ -1262,10 +1274,10 @@ class ScreeningStatistics:
|
|
1262
1274
|
while b > round(a/3):
|
1263
1275
|
#print("Let's search for RIGHT SIDE CUP with width = ", b)
|
1264
1276
|
datC = dat.loc[Db_index:Db_index+b+1]
|
1265
|
-
Dc_index = datC[datC[
|
1266
|
-
Dc_value = datC[datC[
|
1267
|
-
Pc_index = datC[datC[
|
1268
|
-
Pc_value = datC[datC[
|
1277
|
+
Dc_index = datC[datC["close"] == max(datC["close"])]['Date'].index[0]
|
1278
|
+
Dc_value = datC[datC["close"] == max(datC["close"])]['Date'].values[0]
|
1279
|
+
Pc_index = datC[datC["close"] == max(datC["close"])]["close"].index[0]
|
1280
|
+
Pc_value = datC[datC["close"] == max(datC["close"])]["close"].values[0]
|
1269
1281
|
uprv2 = abs(datC.loc[datC['rpv'] > 0, :]['rpv'].mean())
|
1270
1282
|
dprv2 = abs(datC.loc[datC['rpv'] <= 0, :]['rpv'].mean())
|
1271
1283
|
if (dprv2 == 'NaN') | (dprv2 == 0):
|
@@ -1279,10 +1291,10 @@ class ScreeningStatistics:
|
|
1279
1291
|
#print("Let's search for the handle now with width = ", c)
|
1280
1292
|
#print(t, " ", k, " ", a, " ", b, " ", c)
|
1281
1293
|
datD = dat.loc[Dc_index:Dc_index+c+1]
|
1282
|
-
Dd_index = datD[datD[
|
1283
|
-
Dd_value = datD[datD[
|
1284
|
-
Pd_index = datD[datD[
|
1285
|
-
Pd_value = datD[datD[
|
1294
|
+
Dd_index = datD[datD["close"] == min(datD["close"])]['Date'].index[0]
|
1295
|
+
Dd_value = datD[datD["close"] == min(datD["close"])]['Date'].values[0]
|
1296
|
+
Pd_index = datD[datD["close"] == min(datD["close"])]["close"].index[0]
|
1297
|
+
Pd_value = datD[datD["close"] == min(datD["close"])]["close"].values[0]
|
1286
1298
|
uprv3 = abs(datD.loc[datD['rpv'] > 0, :]['rpv'].mean())
|
1287
1299
|
dprv3 = abs(datD.loc[datD['rpv'] <= 0, :]['rpv'].mean())
|
1288
1300
|
if (dprv3 == 'NaN') | (dprv3 == 0):
|
@@ -1310,9 +1322,9 @@ class ScreeningStatistics:
|
|
1310
1322
|
|
1311
1323
|
def validate_cup(self,df, cup_start, cup_bottom, cup_end):
|
1312
1324
|
"""Validate if the detected cup meets shape and depth criteria."""
|
1313
|
-
start_price = df[
|
1314
|
-
bottom_price = df[
|
1315
|
-
end_price = df[
|
1325
|
+
start_price = df["close"].iloc[cup_start]
|
1326
|
+
bottom_price = df["close"].iloc[cup_bottom]
|
1327
|
+
end_price = df["close"].iloc[cup_end]
|
1316
1328
|
|
1317
1329
|
# Cup Depth should be reasonable (10% - 50% drop from highs)
|
1318
1330
|
depth = (start_price - bottom_price) / start_price
|
@@ -1327,7 +1339,7 @@ class ScreeningStatistics:
|
|
1327
1339
|
|
1328
1340
|
# U-shape validation (Avoiding V-bottoms)
|
1329
1341
|
midpoint = (cup_start + cup_end) // 2
|
1330
|
-
if df[
|
1342
|
+
if df["close"].iloc[midpoint] < bottom_price * 1.05:
|
1331
1343
|
return False
|
1332
1344
|
|
1333
1345
|
return True
|
@@ -1337,18 +1349,18 @@ class ScreeningStatistics:
|
|
1337
1349
|
avg_volatility = df['Volatility'].mean()
|
1338
1350
|
|
1339
1351
|
# If volatility is high, require more data points to confirm a cup
|
1340
|
-
if avg_volatility > df[
|
1341
|
-
return int(df[
|
1342
|
-
elif avg_volatility < df[
|
1343
|
-
return int(df[
|
1352
|
+
if avg_volatility > df["close"].mean() * 0.02:
|
1353
|
+
return int(df["close"].mean() * 0.2) + 1 # Higher volatility → require more confirmation
|
1354
|
+
elif avg_volatility < df["close"].mean() * 0.005:
|
1355
|
+
return int(df["close"].mean() * 0.05) + 1 # Lower volatility → allow faster pattern detection
|
1344
1356
|
else:
|
1345
1357
|
return 15 # Default case
|
1346
1358
|
|
1347
1359
|
def validate_volume(self,df, cup_start, cup_end, handle_end):
|
1348
1360
|
"""Ensure decreasing volume in the cup and increasing volume at breakout."""
|
1349
|
-
avg_cup_volume = df[
|
1350
|
-
avg_handle_volume = df[
|
1351
|
-
breakout_volume = df[
|
1361
|
+
avg_cup_volume = df["volume"].iloc[cup_start:cup_end].mean()
|
1362
|
+
avg_handle_volume = df["volume"].iloc[cup_end:handle_end].mean()
|
1363
|
+
breakout_volume = df["volume"].iloc[handle_end]
|
1352
1364
|
|
1353
1365
|
return avg_cup_volume > avg_handle_volume and breakout_volume > avg_handle_volume
|
1354
1366
|
|
@@ -1358,7 +1370,7 @@ class ScreeningStatistics:
|
|
1358
1370
|
from scipy.signal import argrelextrema
|
1359
1371
|
except:
|
1360
1372
|
return False, None
|
1361
|
-
close_prices = df[
|
1373
|
+
close_prices = df["close"].values
|
1362
1374
|
if order <=0:
|
1363
1375
|
order = self.get_dynamic_order(df) # Set order dynamically
|
1364
1376
|
local_min_idx = argrelextrema(close_prices, np.less, order=order)[0] # Local minima (potential cup bottoms)
|
@@ -1375,19 +1387,19 @@ class ScreeningStatistics:
|
|
1375
1387
|
|
1376
1388
|
# Handle Detection
|
1377
1389
|
handle_start = cup_end
|
1378
|
-
potential_handle = df[
|
1390
|
+
potential_handle = df["close"][handle_start:handle_start+15]
|
1379
1391
|
handle_min = potential_handle.min()
|
1380
1392
|
handle_end = potential_handle.idxmin()
|
1381
1393
|
|
1382
1394
|
# Handle should not drop more than 50% of cup depth
|
1383
|
-
cup_depth = df[
|
1384
|
-
handle_depth = df[
|
1395
|
+
cup_depth = df["close"].iloc[cup_start] - df["close"].iloc[cup_bottom]
|
1396
|
+
handle_depth = df["close"].iloc[handle_start] - handle_min
|
1385
1397
|
if handle_depth > cup_depth * 0.5:
|
1386
1398
|
return False,None
|
1387
1399
|
|
1388
1400
|
# Breakout Confirmation
|
1389
|
-
breakout_level = df[
|
1390
|
-
breakout = df[df.index > handle_end][
|
1401
|
+
breakout_level = df["close"].iloc[cup_start]
|
1402
|
+
breakout = df[df.index > handle_end]["close"].gt(breakout_level).any()
|
1391
1403
|
if not breakout:
|
1392
1404
|
return False,None
|
1393
1405
|
|
@@ -1423,7 +1435,7 @@ class ScreeningStatistics:
|
|
1423
1435
|
recent = data.head(2)
|
1424
1436
|
if len(recent) < 2:
|
1425
1437
|
return False
|
1426
|
-
return recent["
|
1438
|
+
return recent["open"].iloc[0] > recent["high"].iloc[1]
|
1427
1439
|
|
1428
1440
|
# Find stocks that opened higher than the previous high
|
1429
1441
|
def findHigherOpens(self, df):
|
@@ -1435,7 +1447,7 @@ class ScreeningStatistics:
|
|
1435
1447
|
recent = data.head(2)
|
1436
1448
|
if len(recent) < 2:
|
1437
1449
|
return False
|
1438
|
-
return recent["
|
1450
|
+
return recent["open"].iloc[0] > recent["close"].iloc[1]
|
1439
1451
|
|
1440
1452
|
# Find DEEL Momentum
|
1441
1453
|
def findHighMomentum(self, df, strict=False):
|
@@ -1446,12 +1458,12 @@ class ScreeningStatistics:
|
|
1446
1458
|
data = data.fillna(0)
|
1447
1459
|
data = data.replace([np.inf, -np.inf], 0)
|
1448
1460
|
data = data[::-1] # Reverse the dataframe so that its the oldest date first
|
1449
|
-
mfis = pktalib.MFI(data["
|
1450
|
-
ccis = pktalib.CCI(data["
|
1451
|
-
sma7 = pktalib.SMA(data["
|
1452
|
-
sma20 = pktalib.SMA(data["
|
1461
|
+
mfis = pktalib.MFI(data["high"],data["low"],data["close"],data["volume"], 14)
|
1462
|
+
ccis = pktalib.CCI(data["high"],data["low"],data["close"], 14)
|
1463
|
+
sma7 = pktalib.SMA(data["close"], 7).tail(2)
|
1464
|
+
sma20 = pktalib.SMA(data["close"], 20).tail(2)
|
1453
1465
|
recent = data.tail(2)
|
1454
|
-
percentChange = round((recent["
|
1466
|
+
percentChange = round((recent["close"].iloc[1] - recent["close"].iloc[0]) *100/recent["close"].iloc[0],1)
|
1455
1467
|
rsi = recent["RSI"].iloc[1]
|
1456
1468
|
mfi = mfis.tail(1).iloc[0]
|
1457
1469
|
cci = ccis.tail(1).iloc[0]
|
@@ -1470,8 +1482,8 @@ class ScreeningStatistics:
|
|
1470
1482
|
# RSI above 68 indicates that the stock is overbought, suggesting that it
|
1471
1483
|
# has increased by more than 68% from its average price over the last 14 days.
|
1472
1484
|
deelMomentum1 = percentChange >= 1 and (rsi>= 68 and mfi >= 68 and cci >= 110)
|
1473
|
-
deelMomentum2 = (rsi>= 50 and mfi >= 50 and recent["
|
1474
|
-
recent["
|
1485
|
+
deelMomentum2 = (rsi>= 50 and mfi >= 50 and recent["close"].iloc[1] >= sma7.iloc[1] and
|
1486
|
+
recent["close"].iloc[1] >= sma20.iloc[1]) and not strict
|
1475
1487
|
hasDeelMomentum = deelMomentum1 or deelMomentum2
|
1476
1488
|
|
1477
1489
|
# if self.shouldLog:
|
@@ -1494,8 +1506,8 @@ class ScreeningStatistics:
|
|
1494
1506
|
diff_df = data[data.index >= pd.to_datetime(f'{PKDateUtilities.tradingDate().strftime(f"%Y-%m-%d")} {MarketHours().openHour:02}:{MarketHours().openMinute+self.configManager.morninganalysiscandlenumber + 2}:00+05:30', utc=True)]
|
1495
1507
|
# brokerSqrOfftime = pd.to_datetime(f'{PKDateUtilities.tradingDate().strftime(f"%Y-%m-%d")} 15:14:00+05:30', utc=True)
|
1496
1508
|
pass
|
1497
|
-
dayHighAfterAlert = diff_df["
|
1498
|
-
highRow = diff_df[diff_df["
|
1509
|
+
dayHighAfterAlert = diff_df["high"].max()
|
1510
|
+
highRow = diff_df[diff_df["high"] >= dayHighAfterAlert]
|
1499
1511
|
if highRow is not None and len(highRow) > 0:
|
1500
1512
|
highRow = highRow.tail(1)
|
1501
1513
|
return highRow.index[-1], highRow
|
@@ -1507,8 +1519,8 @@ class ScreeningStatistics:
|
|
1507
1519
|
data = data.fillna(0)
|
1508
1520
|
data = data.replace([np.inf, -np.inf], 0)
|
1509
1521
|
previousDay = data.head(1)
|
1510
|
-
prevDayHigh = previousDay["
|
1511
|
-
prevDayLow = previousDay["
|
1522
|
+
prevDayHigh = previousDay["high"].iloc[0]
|
1523
|
+
prevDayLow = previousDay["low"].iloc[0]
|
1512
1524
|
candleDurations = [1,5,10,15,30]
|
1513
1525
|
int_df = None
|
1514
1526
|
hasIntradaySetup = False
|
@@ -1519,13 +1531,13 @@ class ScreeningStatistics:
|
|
1519
1531
|
int_df = df_intraday[df_intraday.index <= pd.to_datetime(f'{PKDateUtilities.tradingDate().strftime(f"%Y-%m-%d")} {MarketHours().openHour:02}:{MarketHours().openMinute+candle1MinuteNumberSinceMarketStarted}:00+05:30', utc=True)]
|
1520
1532
|
pass
|
1521
1533
|
if int_df is not None and len(int_df) > 0:
|
1522
|
-
combinedCandle = {"
|
1523
|
-
"
|
1524
|
-
"Adj Close":int_df["Adj Close"][-1],"
|
1525
|
-
openPrice = combinedCandle["
|
1526
|
-
lowPrice = combinedCandle["
|
1527
|
-
closePrice = combinedCandle["
|
1528
|
-
highPrice = combinedCandle["
|
1534
|
+
combinedCandle = {"open":self.getMorningOpen(int_df), "high":max(int_df["high"]),
|
1535
|
+
"low":min(int_df["low"]),"close":self.getMorningClose(int_df),
|
1536
|
+
"Adj Close":int_df["Adj Close"][-1],"volume":sum(int_df["volume"])}
|
1537
|
+
openPrice = combinedCandle["open"]
|
1538
|
+
lowPrice = combinedCandle["low"]
|
1539
|
+
closePrice = combinedCandle["close"]
|
1540
|
+
highPrice = combinedCandle["high"]
|
1529
1541
|
if buySellAll == 1 or buySellAll == 3:
|
1530
1542
|
hasIntradaySetup = openPrice == lowPrice and openPrice < prevDayHigh and closePrice > prevDayHigh
|
1531
1543
|
elif buySellAll == 2 or buySellAll == 3:
|
@@ -1544,11 +1556,11 @@ class ScreeningStatistics:
|
|
1544
1556
|
data = data.fillna(0)
|
1545
1557
|
data = data.replace([np.inf, -np.inf], 0)
|
1546
1558
|
data = data[::-1] # Reverse the dataframe so that its the oldest date first
|
1547
|
-
data_int = pd.DataFrame(data_int)[
|
1559
|
+
data_int = pd.DataFrame(data_int)["close"].resample('30T', offset='15min').ohlc()
|
1548
1560
|
# data_int = data_int[::-1] # Reverse the dataframe so that its the oldest date first
|
1549
1561
|
if len(data_int) < 5: # we need TMA for period 5
|
1550
1562
|
return False
|
1551
|
-
data.loc[:,'PSAR'] = pktalib.psar(data["
|
1563
|
+
data.loc[:,'PSAR'] = pktalib.psar(data["high"],data["low"],acceleration=0.08)
|
1552
1564
|
data_int.loc[:,'TMA5'] = pktalib.TriMA(data_int["close"],length=5)
|
1553
1565
|
recent = data.tail(4)
|
1554
1566
|
recent = recent[::-1]
|
@@ -1561,9 +1573,9 @@ class ScreeningStatistics:
|
|
1561
1573
|
cond1 = recent["PSAR"].iloc[0] >= recent_i["TMA5"].iloc[0] and \
|
1562
1574
|
recent["PSAR"].iloc[1] <= recent_i["TMA5"].iloc[1]
|
1563
1575
|
# Daily volume > 1400k
|
1564
|
-
cond2 = cond1 and (recent["
|
1576
|
+
cond2 = cond1 and (recent["volume"].iloc[0] > 1400000)
|
1565
1577
|
# Daily close above 50
|
1566
|
-
cond4 = cond2 and recent["
|
1578
|
+
cond4 = cond2 and recent["close"].iloc[0] > 50
|
1567
1579
|
return cond4
|
1568
1580
|
|
1569
1581
|
def findIPOLifetimeFirstDayBullishBreak(self, df):
|
@@ -1574,7 +1586,7 @@ class ScreeningStatistics:
|
|
1574
1586
|
data = data.replace([np.inf, -np.inf], 0)
|
1575
1587
|
data.dropna(axis=0, how="all", inplace=True) # Maybe there was no trade done at these times?
|
1576
1588
|
data = data[::-1] # Reverse the dataframe so that its the oldest date first
|
1577
|
-
return data["
|
1589
|
+
return data["high"].iloc[0] >= data["high"].max()
|
1578
1590
|
|
1579
1591
|
def findMACDCrossover(self, df, afterTimestamp=None, nthCrossover=1, upDirection=True, minRSI=60):
|
1580
1592
|
if df is None or len(df) == 0:
|
@@ -1584,11 +1596,11 @@ class ScreeningStatistics:
|
|
1584
1596
|
data = data.replace([np.inf, -np.inf], 0)
|
1585
1597
|
data.dropna(axis=0, how="all", inplace=True) # Maybe there was no trade done at these times?
|
1586
1598
|
data = data[::-1] # Reverse the dataframe so that its the oldest date first
|
1587
|
-
macdLine, macdSignal, macdHist = pktalib.MACD(data["
|
1588
|
-
# rsi_df = pktalib.RSI(data["
|
1599
|
+
macdLine, macdSignal, macdHist = pktalib.MACD(data["close"], 12, 26, 9)
|
1600
|
+
# rsi_df = pktalib.RSI(data["close"], 14)
|
1589
1601
|
line_df = pd.DataFrame(macdLine)
|
1590
1602
|
signal_df = pd.DataFrame(macdSignal)
|
1591
|
-
vol_df = data["
|
1603
|
+
vol_df = data["volume"]
|
1592
1604
|
diff_df = pd.concat([line_df, signal_df, signal_df-line_df,vol_df], axis=1)
|
1593
1605
|
diff_df.columns = ["line","signal","diff","vol"]
|
1594
1606
|
diff_df = diff_df[diff_df["vol"] > 0] # We're not going to do anything with a candle where there was no trade.
|
@@ -1626,27 +1638,27 @@ class ScreeningStatistics:
|
|
1626
1638
|
return False
|
1627
1639
|
data = df.copy()
|
1628
1640
|
# https://chartink.com/screener/nr4-daily-today
|
1629
|
-
if data.tail(1)["
|
1641
|
+
if data.tail(1)["volume"].iloc[0] <= 50000:
|
1630
1642
|
return False
|
1631
1643
|
data = data.fillna(0)
|
1632
1644
|
data = data.replace([np.inf, -np.inf], 0)
|
1633
1645
|
data = data[::-1] # Reverse the dataframe so that its the oldest date first
|
1634
|
-
data["SMA10"] = pktalib.SMA(data["
|
1635
|
-
data["SMA50"] = pktalib.SMA(data["
|
1636
|
-
data["SMA200"] = pktalib.SMA(data["
|
1646
|
+
data["SMA10"] = pktalib.SMA(data["close"], 10)
|
1647
|
+
data["SMA50"] = pktalib.SMA(data["close"], 50)
|
1648
|
+
data["SMA200"] = pktalib.SMA(data["close"], 200)
|
1637
1649
|
recent = data.tail(5)
|
1638
1650
|
recent = recent[::-1]
|
1639
|
-
cond1 = (recent["
|
1640
|
-
recent["
|
1651
|
+
cond1 = (recent["high"].iloc[0] - recent["low"].iloc[0]) < (
|
1652
|
+
recent["high"].iloc[1] - recent["low"].iloc[1]
|
1641
1653
|
)
|
1642
|
-
cond2 = cond1 and (recent["
|
1643
|
-
recent["
|
1654
|
+
cond2 = cond1 and (recent["high"].iloc[0] - recent["low"].iloc[0]) < (
|
1655
|
+
recent["high"].iloc[2] - recent["low"].iloc[2]
|
1644
1656
|
)
|
1645
|
-
cond3 = cond2 and (recent["
|
1646
|
-
recent["
|
1657
|
+
cond3 = cond2 and (recent["high"].iloc[0] - recent["low"].iloc[0]) < (
|
1658
|
+
recent["high"].iloc[3] - recent["low"].iloc[3]
|
1647
1659
|
)
|
1648
|
-
cond4 = cond3 and (recent["
|
1649
|
-
recent["
|
1660
|
+
cond4 = cond3 and (recent["high"].iloc[0] - recent["low"].iloc[0]) < (
|
1661
|
+
recent["high"].iloc[4] - recent["low"].iloc[4]
|
1650
1662
|
)
|
1651
1663
|
cond5 = cond4 and (recent["SMA10"].iloc[0] > recent["SMA50"].iloc[0])
|
1652
1664
|
cond6 = cond5 and (recent["SMA50"].iloc[0] > recent["SMA200"].iloc[0])
|
@@ -1659,22 +1671,22 @@ class ScreeningStatistics:
|
|
1659
1671
|
data = data.fillna(0)
|
1660
1672
|
data = data.replace([np.inf, -np.inf], 0)
|
1661
1673
|
data = data[::-1] # Reverse the dataframe so that its the oldest date first
|
1662
|
-
data.loc[:,'BBands-U'], data.loc[:,'BBands-M'], data.loc[:,'BBands-L'] = pktalib.BBANDS(data["
|
1674
|
+
data.loc[:,'BBands-U'], data.loc[:,'BBands-M'], data.loc[:,'BBands-L'] = pktalib.BBANDS(data["close"], 20)
|
1663
1675
|
recent = data.tail(4)
|
1664
1676
|
recent = recent[::-1]
|
1665
1677
|
if len(recent) < 4:
|
1666
1678
|
return False
|
1667
1679
|
# 1 day ago high > 2 days ago high
|
1668
|
-
cond1 = recent["
|
1680
|
+
cond1 = recent["high"].iloc[1] > recent["high"].iloc[2]
|
1669
1681
|
# 1 day ago close < 2 days ago high
|
1670
|
-
cond2 = cond1 and (recent["
|
1682
|
+
cond2 = cond1 and (recent["close"].iloc[1] < recent["high"].iloc[2])
|
1671
1683
|
# 1 day ago volume > 3 days ago volume
|
1672
|
-
cond3 = cond2 and (recent["
|
1684
|
+
cond3 = cond2 and (recent["volume"].iloc[1] > recent["volume"].iloc[3])
|
1673
1685
|
# daily high < 1 day ago high
|
1674
|
-
cond4 = cond3 and (recent["
|
1686
|
+
cond4 = cond3 and (recent["high"].iloc[0] < recent["high"].iloc[1])
|
1675
1687
|
# daily close crossed below daily lower bollinger band(20,2)
|
1676
|
-
cond5 = cond4 and (recent["
|
1677
|
-
recent["
|
1688
|
+
cond5 = cond4 and (recent["close"].iloc[0] <= recent["BBands-L"].iloc[0] and \
|
1689
|
+
recent["close"].iloc[1] >= recent["BBands-L"].iloc[1])
|
1678
1690
|
return cond5
|
1679
1691
|
|
1680
1692
|
# Find potential breakout stocks
|
@@ -1691,17 +1703,17 @@ class ScreeningStatistics:
|
|
1691
1703
|
data = data.replace([np.inf, -np.inf], 0)
|
1692
1704
|
data = data.head(231)
|
1693
1705
|
recent = data.head(1)
|
1694
|
-
recentVolume = recent["
|
1695
|
-
recentClose = round(recent["
|
1696
|
-
highestHigh200 = round(data.head(201).tail(200).describe()["
|
1697
|
-
highestHigh30 = round(data.head(31).tail(30).describe()["
|
1698
|
-
highestHigh200From30 = round(data.tail(200).describe()["
|
1699
|
-
highestHigh8From30 = round(data.head(39).tail(8).describe()["
|
1706
|
+
recentVolume = recent["volume"].iloc[0]
|
1707
|
+
recentClose = round(recent["close"].iloc[0] * 1.05, 2)
|
1708
|
+
highestHigh200 = round(data.head(201).tail(200).describe()["high"]["max"], 2)
|
1709
|
+
highestHigh30 = round(data.head(31).tail(30).describe()["high"]["max"], 2)
|
1710
|
+
highestHigh200From30 = round(data.tail(200).describe()["high"]["max"], 2)
|
1711
|
+
highestHigh8From30 = round(data.head(39).tail(8).describe()["high"]["max"], 2)
|
1700
1712
|
data = data.head(200)
|
1701
1713
|
data = data[::-1] # Reverse the dataframe so that its the oldest date first
|
1702
|
-
vol200 = pktalib.SMA(data["
|
1714
|
+
vol200 = pktalib.SMA(data["volume"],timeperiod=200)
|
1703
1715
|
data["SMA200V"] = vol200
|
1704
|
-
vol50 = pktalib.SMA(data["
|
1716
|
+
vol50 = pktalib.SMA(data["volume"],timeperiod=50)
|
1705
1717
|
data["SMA50V"] = vol50
|
1706
1718
|
recent = data.tail(1)
|
1707
1719
|
sma200v = recent["SMA200V"].iloc[0]
|
@@ -1731,7 +1743,7 @@ class ScreeningStatistics:
|
|
1731
1743
|
return False
|
1732
1744
|
|
1733
1745
|
def findPriceActionCross(self, df, ma, daysToConsider=1, baseMAOrPrice=None, isEMA=False,maDirectionFromBelow=True):
|
1734
|
-
ma_val = pktalib.EMA(df["
|
1746
|
+
ma_val = pktalib.EMA(df["close"],int(ma)) if isEMA else pktalib.SMA(df["close"],int(ma))
|
1735
1747
|
ma = ma_val.tail(daysToConsider).head(1).iloc[0]
|
1736
1748
|
ma_prev = ma_val.tail(daysToConsider+1).head(1).iloc[0]
|
1737
1749
|
base = baseMAOrPrice.tail(daysToConsider).head(1).iloc[0]
|
@@ -1754,13 +1766,13 @@ class ScreeningStatistics:
|
|
1754
1766
|
if len(recent) < 4:
|
1755
1767
|
return False
|
1756
1768
|
# 1 day ago high > 2 days ago high
|
1757
|
-
cond1 = recent["
|
1769
|
+
cond1 = recent["high"].iloc[1] > recent["high"].iloc[2]
|
1758
1770
|
# daily close < 1 day ago high
|
1759
|
-
cond2 = cond1 and (recent["
|
1771
|
+
cond2 = cond1 and (recent["close"].iloc[0] < recent["high"].iloc[1])
|
1760
1772
|
# Daily volume > 3 days ago volume
|
1761
|
-
cond3 = cond2 and (recent["
|
1773
|
+
cond3 = cond2 and (recent["volume"].iloc[0] > recent["volume"].iloc[3])
|
1762
1774
|
# daily high < 1 day ago high
|
1763
|
-
cond4 = cond3 and (recent["
|
1775
|
+
cond4 = cond3 and (recent["high"].iloc[0] < recent["high"].iloc[1])
|
1764
1776
|
return cond4
|
1765
1777
|
|
1766
1778
|
# Find stocks with reversing PSAR and RSI
|
@@ -1769,7 +1781,7 @@ class ScreeningStatistics:
|
|
1769
1781
|
return False
|
1770
1782
|
data = df.copy()
|
1771
1783
|
data = data[::-1]
|
1772
|
-
psar = pktalib.psar(data["
|
1784
|
+
psar = pktalib.psar(data["high"],data["low"])
|
1773
1785
|
if len(psar) < 3:
|
1774
1786
|
return False
|
1775
1787
|
psar = psar.tail(3)
|
@@ -1777,9 +1789,9 @@ class ScreeningStatistics:
|
|
1777
1789
|
# dayMinus2Psar = psar.iloc[0]
|
1778
1790
|
dayMinus1Psar = psar.iloc[1]
|
1779
1791
|
dayPSAR = psar.iloc[2]
|
1780
|
-
# dayMinus2Close = data["
|
1781
|
-
dayMinus1Close = data["
|
1782
|
-
dayClose = data["
|
1792
|
+
# dayMinus2Close = data["close"].iloc[0]
|
1793
|
+
dayMinus1Close = data["close"].iloc[1]
|
1794
|
+
dayClose = data["close"].iloc[2]
|
1783
1795
|
# dayMinus2RSI = data["RSI"].iloc[0]
|
1784
1796
|
dayMinus1RSI = data["RSI"].iloc[1]
|
1785
1797
|
dayRSI = data["RSI"].iloc[2]
|
@@ -1818,9 +1830,9 @@ class ScreeningStatistics:
|
|
1818
1830
|
for maLength in maRange:
|
1819
1831
|
dataCopy = data
|
1820
1832
|
if self.configManager.useEMA or maLength == 9:
|
1821
|
-
maRev = pktalib.EMA(dataCopy["
|
1833
|
+
maRev = pktalib.EMA(dataCopy["close"], timeperiod=maLength)
|
1822
1834
|
else:
|
1823
|
-
maRev = pktalib.MA(dataCopy["
|
1835
|
+
maRev = pktalib.MA(dataCopy["close"], timeperiod=maLength)
|
1824
1836
|
try:
|
1825
1837
|
dataCopy.drop("maRev", axis=1, inplace=True, errors="ignore")
|
1826
1838
|
except KeyboardInterrupt: # pragma: no cover
|
@@ -1832,11 +1844,11 @@ class ScreeningStatistics:
|
|
1832
1844
|
bullishMAReversal = dataCopy["maRev"].iloc[0] >= dataCopy["maRev"].iloc[1] and \
|
1833
1845
|
dataCopy["maRev"].iloc[1] >= dataCopy["maRev"].iloc[2] and \
|
1834
1846
|
dataCopy["maRev"].iloc[2] < dataCopy["maRev"].iloc[3]
|
1835
|
-
bullishClose = dataCopy.head(1)["
|
1847
|
+
bullishClose = dataCopy.head(1)["close"].iloc[0] >= dataCopy.head(1)["maRev"].iloc[0]
|
1836
1848
|
bearishMAReversal = dataCopy["maRev"].iloc[0] <= dataCopy["maRev"].iloc[1] and \
|
1837
1849
|
dataCopy["maRev"].iloc[1] <= dataCopy["maRev"].iloc[2] and \
|
1838
1850
|
dataCopy["maRev"].iloc[2] > dataCopy["maRev"].iloc[3]
|
1839
|
-
isRecentCloseWithinPercentRange = dataCopy.equals(dataCopy[(dataCopy.
|
1851
|
+
isRecentCloseWithinPercentRange = dataCopy.equals(dataCopy[(dataCopy.close >= (dataCopy.maRev - (dataCopy.maRev * percentage))) & (dataCopy.close <= (dataCopy.maRev + (dataCopy.maRev * percentage)))])
|
1840
1852
|
if (isRecentCloseWithinPercentRange and bullishClose and bullishMAReversal) or \
|
1841
1853
|
(isRecentCloseWithinPercentRange and not bullishClose and bearishMAReversal):
|
1842
1854
|
hasReversals = True
|
@@ -1906,7 +1918,7 @@ class ScreeningStatistics:
|
|
1906
1918
|
if df is None or len(df) == 0 or len(df) < 144:
|
1907
1919
|
return 0
|
1908
1920
|
# RVM over the lookback period of 15 periods
|
1909
|
-
rvm = pktalib.RVM(df["
|
1921
|
+
rvm = pktalib.RVM(df["high"],df["low"],df["close"],15)
|
1910
1922
|
screenDict["RVM(15)"] = rvm
|
1911
1923
|
saveDict["RVM(15)"] = rvm
|
1912
1924
|
return rvm
|
@@ -1918,20 +1930,20 @@ class ScreeningStatistics:
|
|
1918
1930
|
data = data.fillna(0)
|
1919
1931
|
data = data.replace([np.inf, -np.inf], 0)
|
1920
1932
|
data = data[::-1] # Reverse the dataframe so that its the oldest date first
|
1921
|
-
data.loc[:,'SMAV10'] = pktalib.SMA(data["
|
1933
|
+
data.loc[:,'SMAV10'] = pktalib.SMA(data["volume"], 10)
|
1922
1934
|
recent = data.tail(4)
|
1923
1935
|
recent = recent[::-1]
|
1924
1936
|
if len(recent) < 4:
|
1925
1937
|
return False
|
1926
1938
|
# daily close < 1 day ago close * .97
|
1927
|
-
cond1 = recent["
|
1939
|
+
cond1 = recent["close"].iloc[0] < recent["close"].iloc[1] * 0.97
|
1928
1940
|
# Daily volume > 100k
|
1929
|
-
cond2 = cond1 and (recent["
|
1941
|
+
cond2 = cond1 and (recent["volume"].iloc[0] > 100000)
|
1930
1942
|
# Daily volume * Daily Close > 1000k
|
1931
|
-
cond3 = cond2 and (recent["
|
1943
|
+
cond3 = cond2 and (recent["volume"].iloc[0] * recent["close"].iloc[0] > 1000000)
|
1932
1944
|
# Daily close above 8
|
1933
|
-
cond4 = cond3 and recent["
|
1934
|
-
cond5 = cond4 and (recent["
|
1945
|
+
cond4 = cond3 and recent["close"].iloc[0] > 8
|
1946
|
+
cond5 = cond4 and (recent["volume"].iloc[0] > recent["SMAV10"].iloc[0] * 0.75)
|
1935
1947
|
return cond5
|
1936
1948
|
|
1937
1949
|
def findSuperGainersLosers(self, df, percentChangeRequired=15, gainer=True):
|
@@ -1942,7 +1954,7 @@ class ScreeningStatistics:
|
|
1942
1954
|
data = data.replace([np.inf, -np.inf], 0)
|
1943
1955
|
data = data[::-1] # Reverse the dataframe so that its the oldest date first
|
1944
1956
|
recent = data.tail(2)
|
1945
|
-
percentChange = round((recent["
|
1957
|
+
percentChange = round((recent["close"].iloc[1] - recent["close"].iloc[0]) *100/recent["close"].iloc[0],1)
|
1946
1958
|
return percentChange >= percentChangeRequired if gainer else percentChange <= percentChangeRequired
|
1947
1959
|
|
1948
1960
|
#@measure_time
|
@@ -1961,10 +1973,10 @@ class ScreeningStatistics:
|
|
1961
1973
|
saved = self.findCurrentSavedValue(screenDict,saveDict,"Trend")
|
1962
1974
|
try:
|
1963
1975
|
with SuppressOutput(suppress_stdout=True, suppress_stderr=True):
|
1964
|
-
data["tops"] = data["
|
1976
|
+
data["tops"] = data["close"].iloc[
|
1965
1977
|
list(
|
1966
1978
|
pktalib.argrelextrema(
|
1967
|
-
np.array(data["
|
1979
|
+
np.array(data["close"]), np.greater_equal, order=1
|
1968
1980
|
)[0]
|
1969
1981
|
)
|
1970
1982
|
]
|
@@ -2046,16 +2058,16 @@ class ScreeningStatistics:
|
|
2046
2058
|
|
2047
2059
|
""" Ignoring the Resitance for long-term purpose
|
2048
2060
|
while len(data_high) > points:
|
2049
|
-
slope, intercept, r_value, p_value, std_err = linregress(x=data_high['Number'], y=data_high[
|
2050
|
-
data_high = data_high.loc[data_high[
|
2051
|
-
slope, intercept, r_value, p_value, std_err = linregress(x=data_high['Number'], y=data_high[
|
2061
|
+
slope, intercept, r_value, p_value, std_err = linregress(x=data_high['Number'], y=data_high["high"])
|
2062
|
+
data_high = data_high.loc[data_high["high"] > slope * data_high['Number'] + intercept]
|
2063
|
+
slope, intercept, r_value, p_value, std_err = linregress(x=data_high['Number'], y=data_high["close"])
|
2052
2064
|
data['Resistance'] = slope * data['Number'] + intercept
|
2053
2065
|
"""
|
2054
2066
|
|
2055
2067
|
while len(data_low) > points:
|
2056
2068
|
try:
|
2057
2069
|
slope, intercept, r_value, p_value, std_err = linregress(
|
2058
|
-
x=data_low["Number"], y=data_low["
|
2070
|
+
x=data_low["Number"], y=data_low["low"]
|
2059
2071
|
)
|
2060
2072
|
except KeyboardInterrupt: # pragma: no cover
|
2061
2073
|
raise KeyboardInterrupt
|
@@ -2063,11 +2075,11 @@ class ScreeningStatistics:
|
|
2063
2075
|
self.default_logger.debug(e, exc_info=True)
|
2064
2076
|
continue
|
2065
2077
|
data_low = data_low.loc[
|
2066
|
-
data_low["
|
2078
|
+
data_low["low"] < slope * data_low["Number"] + intercept
|
2067
2079
|
]
|
2068
2080
|
|
2069
2081
|
slope, intercept, r_value, p_value, std_err = linregress(
|
2070
|
-
x=data_low["Number"], y=data_low["
|
2082
|
+
x=data_low["Number"], y=data_low["close"]
|
2071
2083
|
)
|
2072
2084
|
data["Support"] = slope * data["Number"] + intercept
|
2073
2085
|
now = data.tail(1)
|
@@ -2075,7 +2087,7 @@ class ScreeningStatistics:
|
|
2075
2087
|
limit_upper = now["Support"].iloc[0] + (now["Support"].iloc[0] * percentage)
|
2076
2088
|
limit_lower = now["Support"].iloc[0] - (now["Support"].iloc[0] * percentage)
|
2077
2089
|
saved = self.findCurrentSavedValue(screenDict, saveDict, "Pattern")
|
2078
|
-
if limit_lower < now["
|
2090
|
+
if limit_lower < now["close"].iloc[0] < limit_upper and slope > 0.15:
|
2079
2091
|
screenDict["Pattern"] = (
|
2080
2092
|
saved[0] + colorText.GREEN + "Trendline-Support" + colorText.END
|
2081
2093
|
)
|
@@ -2088,7 +2100,7 @@ class ScreeningStatistics:
|
|
2088
2100
|
color = 'tab:green'
|
2089
2101
|
xdate = [x.date() for x in data.index]
|
2090
2102
|
ax1.set_xlabel('Date', color=color)
|
2091
|
-
ax1.plot(xdate, data.
|
2103
|
+
ax1.plot(xdate, data.close, label="close", color=color)
|
2092
2104
|
ax1.tick_params(axis='x', labelcolor=color)
|
2093
2105
|
|
2094
2106
|
ax2 = ax1.twiny() # ax2 and ax1 will have common y axis and different x axis, twiny
|
@@ -2118,14 +2130,14 @@ class ScreeningStatistics:
|
|
2118
2130
|
try:
|
2119
2131
|
data = df.copy()
|
2120
2132
|
data = data[::-1]
|
2121
|
-
today_sma = pktalib.SMA(data["
|
2122
|
-
sma_minus9 = pktalib.SMA(data.head(len(data)-9)["
|
2123
|
-
sma_minus14 = pktalib.SMA(data.head(len(data)-14)["
|
2124
|
-
sma_minus20 = pktalib.SMA(data.head(len(data)-20)["
|
2125
|
-
today_lma = pktalib.SMA(data["
|
2126
|
-
lma_minus20 = pktalib.SMA(data.head(len(data)-20)["
|
2127
|
-
lma_minus80 = pktalib.SMA(data.head(len(data)-80)["
|
2128
|
-
lma_minus100 = pktalib.SMA(data.head(len(data)-100)["
|
2133
|
+
today_sma = pktalib.SMA(data["close"], timeperiod=50)
|
2134
|
+
sma_minus9 = pktalib.SMA(data.head(len(data)-9)["close"], timeperiod=50)
|
2135
|
+
sma_minus14 = pktalib.SMA(data.head(len(data)-14)["close"], timeperiod=50)
|
2136
|
+
sma_minus20 = pktalib.SMA(data.head(len(data)-20)["close"], timeperiod=50)
|
2137
|
+
today_lma = pktalib.SMA(data["close"], timeperiod=200)
|
2138
|
+
lma_minus20 = pktalib.SMA(data.head(len(data)-20)["close"], timeperiod=200)
|
2139
|
+
lma_minus80 = pktalib.SMA(data.head(len(data)-80)["close"], timeperiod=200)
|
2140
|
+
lma_minus100 = pktalib.SMA(data.head(len(data)-100)["close"], timeperiod=200)
|
2129
2141
|
today_lma = today_lma.iloc[len(today_lma)-1] if today_lma is not None else 0
|
2130
2142
|
lma_minus20 = lma_minus20.iloc[len(lma_minus20)-1] if lma_minus20 is not None else 0
|
2131
2143
|
lma_minus80 = lma_minus80.iloc[len(lma_minus80)-1] if lma_minus80 is not None else 0
|
@@ -2229,13 +2241,13 @@ class ScreeningStatistics:
|
|
2229
2241
|
return isUptrend, mf_inst_ownershipChange, fairValueDiff
|
2230
2242
|
|
2231
2243
|
def getCandleBodyHeight(self, dailyData):
|
2232
|
-
bodyHeight = dailyData["
|
2244
|
+
bodyHeight = dailyData["close"].iloc[0] - dailyData["open"].iloc[0]
|
2233
2245
|
return bodyHeight
|
2234
2246
|
|
2235
2247
|
# Private method to find candle type
|
2236
2248
|
# True = Bullish, False = Bearish
|
2237
2249
|
def getCandleType(self, dailyData):
|
2238
|
-
return bool(dailyData["
|
2250
|
+
return bool(dailyData["close"].iloc[0] >= dailyData["open"].iloc[0])
|
2239
2251
|
|
2240
2252
|
def getFairValue(self, stock, hostData=None, force=False,exchangeName="INDIA"):
|
2241
2253
|
if hostData is None or len(hostData) < 1:
|
@@ -2336,18 +2348,18 @@ class ScreeningStatistics:
|
|
2336
2348
|
return netChangeMF,netChangeInst,latest_mfdate,latest_instdate
|
2337
2349
|
|
2338
2350
|
def getMorningClose(self,df):
|
2339
|
-
close = df["
|
2351
|
+
close = df["close"][-1]
|
2340
2352
|
index = len(df)
|
2341
2353
|
while close is np.nan and index >= 0:
|
2342
|
-
close = df["
|
2354
|
+
close = df["close"][index - 1]
|
2343
2355
|
index -= 1
|
2344
2356
|
return close
|
2345
2357
|
|
2346
2358
|
def getMorningOpen(self,df):
|
2347
|
-
open = df["
|
2359
|
+
open = df["open"][0]
|
2348
2360
|
index = 0
|
2349
2361
|
while open is np.nan and index < len(df):
|
2350
|
-
open = df["
|
2362
|
+
open = df["open"][index + 1]
|
2351
2363
|
index += 1
|
2352
2364
|
return open
|
2353
2365
|
|
@@ -2451,10 +2463,10 @@ class ScreeningStatistics:
|
|
2451
2463
|
with SuppressOutput(suppress_stderr=True, suppress_stdout=True):
|
2452
2464
|
data = data[pkl["columns"]]
|
2453
2465
|
### v2 Preprocessing
|
2454
|
-
data["
|
2455
|
-
data["
|
2456
|
-
data["
|
2457
|
-
data["
|
2466
|
+
data["high"] = data["high"].pct_change() * 100
|
2467
|
+
data["low"] = data["low"].pct_change() * 100
|
2468
|
+
data["open"] = data["open"].pct_change() * 100
|
2469
|
+
data["close"] = data["close"].pct_change() * 100
|
2458
2470
|
data = data.iloc[-1]
|
2459
2471
|
###
|
2460
2472
|
data = pkl["scaler"].transform([data])
|
@@ -2512,47 +2524,47 @@ class ScreeningStatistics:
|
|
2512
2524
|
data = data.replace([np.inf, -np.inf], 0)
|
2513
2525
|
data.reset_index(inplace=True)
|
2514
2526
|
data.rename(columns={"index": "Date"}, inplace=True)
|
2515
|
-
data = data[data["
|
2516
|
-
data = data[data["
|
2517
|
-
data["tops"] = (data["
|
2518
|
-
data["bots"] = (data["
|
2527
|
+
data = data[data["high"]>0]
|
2528
|
+
data = data[data["low"]>0]
|
2529
|
+
data["tops"] = (data["high"].iloc[list(pktalib.argrelextrema(np.array(data["high"]), np.greater_equal, order=window)[0])].head(numTopsBottoms))
|
2530
|
+
data["bots"] = (data["low"].iloc[list(pktalib.argrelextrema(np.array(data["low"]), np.less_equal, order=window)[0])].head(numTopsBottoms))
|
2519
2531
|
tops = data[data.tops > 0]
|
2520
2532
|
bots = data[data.bots > 0]
|
2521
2533
|
return tops, bots
|
2522
2534
|
|
2523
2535
|
def monitorFiveEma(self, fetcher, result_df, last_signal, risk_reward=3):
|
2524
|
-
col_names = ["
|
2536
|
+
col_names = ["high", "low", "close", "5EMA"]
|
2525
2537
|
data_list = ["nifty_buy", "banknifty_buy", "nifty_sell", "banknifty_sell"]
|
2526
2538
|
|
2527
2539
|
data_tuple = fetcher.fetchFiveEmaData()
|
2528
2540
|
for cnt in range(len(data_tuple)):
|
2529
2541
|
d = data_tuple[cnt]
|
2530
|
-
d["5EMA"] = pktalib.EMA(d["
|
2542
|
+
d["5EMA"] = pktalib.EMA(d["close"], timeperiod=5)
|
2531
2543
|
d = d[col_names]
|
2532
2544
|
d = d.dropna().round(2)
|
2533
2545
|
|
2534
2546
|
with SuppressOutput(suppress_stderr=True, suppress_stdout=True):
|
2535
2547
|
if "sell" in data_list[cnt]:
|
2536
|
-
streched = d[(d.
|
2537
|
-
streched["SL"] = streched.
|
2548
|
+
streched = d[(d.low > d["5EMA"]) & (d.low - d["5EMA"] > 0.5)]
|
2549
|
+
streched["SL"] = streched.high
|
2538
2550
|
validate = d[
|
2539
|
-
(d.
|
2540
|
-
& (d.
|
2551
|
+
(d.low.shift(1) > d["5EMA"].shift(1))
|
2552
|
+
& (d.low.shift(1) - d["5EMA"].shift(1) > 0.5)
|
2541
2553
|
]
|
2542
2554
|
old_index = validate.index
|
2543
2555
|
else:
|
2544
|
-
mask = (d.
|
2556
|
+
mask = (d.high < d["5EMA"]) & (d["5EMA"] - d.high > 0.5) # Buy
|
2545
2557
|
streched = d[mask]
|
2546
|
-
streched["SL"] = streched.
|
2558
|
+
streched["SL"] = streched.low
|
2547
2559
|
validate = d.loc[mask.shift(1).fillna(False)]
|
2548
2560
|
old_index = validate.index
|
2549
2561
|
tgt = pd.DataFrame(
|
2550
2562
|
(
|
2551
|
-
validate.
|
2563
|
+
validate.close.reset_index(drop=True)
|
2552
2564
|
- (
|
2553
2565
|
(
|
2554
2566
|
streched.SL.reset_index(drop=True)
|
2555
|
-
- validate.
|
2567
|
+
- validate.close.reset_index(drop=True)
|
2556
2568
|
)
|
2557
2569
|
* risk_reward
|
2558
2570
|
)
|
@@ -2570,9 +2582,9 @@ class ScreeningStatistics:
|
|
2570
2582
|
validate = validate.tail(len(old_index))
|
2571
2583
|
validate = validate.set_index(old_index)
|
2572
2584
|
if "sell" in data_list[cnt]:
|
2573
|
-
final = validate[validate.
|
2585
|
+
final = validate[validate.close < validate["5EMA"]].tail(1)
|
2574
2586
|
else:
|
2575
|
-
final = validate[validate.
|
2587
|
+
final = validate[validate.close > validate["5EMA"]].tail(1)
|
2576
2588
|
|
2577
2589
|
if data_list[cnt] not in last_signal:
|
2578
2590
|
last_signal[data_list[cnt]] = final
|
@@ -2639,7 +2651,7 @@ class ScreeningStatistics:
|
|
2639
2651
|
return result_df[::-1]
|
2640
2652
|
|
2641
2653
|
def non_zero_range(self, high: pd.Series, low: pd.Series) -> pd.Series:
|
2642
|
-
"""Returns the difference of two series and adds epsilon to any zero values. This occurs commonly in crypto data when
|
2654
|
+
"""Returns the difference of two series and adds epsilon to any zero values. This occurs commonly in crypto data when "high" = "low"."""
|
2643
2655
|
diff = high - low
|
2644
2656
|
if diff.eq(0).any().any():
|
2645
2657
|
diff += sflt.epsilon
|
@@ -2652,8 +2664,8 @@ class ScreeningStatistics:
|
|
2652
2664
|
(dataframe['adx'] > self.adx_long_min.value) & # trend strength confirmation
|
2653
2665
|
(dataframe['adx'] < self.adx_long_max.value) & # trend strength confirmation
|
2654
2666
|
(dataframe['trend_l'] > 0) &
|
2655
|
-
(dataframe[
|
2656
|
-
(dataframe[
|
2667
|
+
(dataframe["volume"] > dataframe['volume_mean']) &
|
2668
|
+
(dataframe["volume"] > 0)
|
2657
2669
|
|
2658
2670
|
),
|
2659
2671
|
'enter_long'] = 1
|
@@ -2663,7 +2675,7 @@ class ScreeningStatistics:
|
|
2663
2675
|
(dataframe['adx'] > self.adx_short_min.value) & # trend strength confirmation
|
2664
2676
|
(dataframe['adx'] < self.adx_short_max.value) & # trend strength confirmation
|
2665
2677
|
(dataframe['trend_s'] < 0) &
|
2666
|
-
(dataframe[
|
2678
|
+
(dataframe["volume"] > dataframe['volume_mean_s']) # volume weighted indicator
|
2667
2679
|
),
|
2668
2680
|
'enter_short'] = 1
|
2669
2681
|
|
@@ -2676,15 +2688,15 @@ class ScreeningStatistics:
|
|
2676
2688
|
dataframe.loc[:, 'exit_tag'] = ''
|
2677
2689
|
|
2678
2690
|
exit_long = (
|
2679
|
-
# (dataframe[
|
2680
|
-
(dataframe[
|
2681
|
-
(dataframe[
|
2691
|
+
# (dataframe["close"] < dataframe["low"].shift(self.sell_shift.value)) &
|
2692
|
+
(dataframe["close"] < dataframe['ema_l']) &
|
2693
|
+
(dataframe["volume"] > dataframe['volume_mean_exit'])
|
2682
2694
|
)
|
2683
2695
|
|
2684
2696
|
exit_short = (
|
2685
|
-
# (dataframe[
|
2686
|
-
(dataframe[
|
2687
|
-
(dataframe[
|
2697
|
+
# (dataframe["close"] > dataframe["high"].shift(self.sell_shift_short.value)) &
|
2698
|
+
(dataframe["close"] > dataframe['ema_s']) &
|
2699
|
+
(dataframe["volume"] > dataframe['volume_mean_exit_s'])
|
2688
2700
|
)
|
2689
2701
|
|
2690
2702
|
|
@@ -2726,16 +2738,16 @@ class ScreeningStatistics:
|
|
2726
2738
|
# dataframe['rsi'] = ta.RSI(dataframe)
|
2727
2739
|
|
2728
2740
|
# EMA
|
2729
|
-
dataframe['ema_l'] = pktalib.EMA(dataframe[
|
2730
|
-
dataframe['ema_s'] = pktalib.EMA(dataframe[
|
2741
|
+
dataframe['ema_l'] = pktalib.EMA(dataframe["close"], timeperiod=self.ema_period_l_exit.value)
|
2742
|
+
dataframe['ema_s'] = pktalib.EMA(dataframe["close"], timeperiod=self.ema_period_s_exit.value)
|
2731
2743
|
|
2732
2744
|
|
2733
2745
|
# Volume Weighted
|
2734
|
-
dataframe['volume_mean'] = dataframe[
|
2735
|
-
dataframe['volume_mean_exit'] = dataframe[
|
2746
|
+
dataframe['volume_mean'] = dataframe["volume"].rolling(self.volume_check.value).mean().shift(1)
|
2747
|
+
dataframe['volume_mean_exit'] = dataframe["volume"].rolling(self.volume_check_exit.value).mean().shift(1)
|
2736
2748
|
|
2737
|
-
dataframe['volume_mean_s'] = dataframe[
|
2738
|
-
dataframe['volume_mean_exit_s'] = dataframe[
|
2749
|
+
dataframe['volume_mean_s'] = dataframe["volume"].rolling(self.volume_check_s.value).mean().shift(1)
|
2750
|
+
dataframe['volume_mean_exit_s'] = dataframe["volume"].rolling(self.volume_check_exit_s.value).mean().shift(1)
|
2739
2751
|
return dataframe
|
2740
2752
|
|
2741
2753
|
# Preprocess the acquired data
|
@@ -2749,36 +2761,36 @@ class ScreeningStatistics:
|
|
2749
2761
|
# self.default_logger.info(f"Preprocessing data:\n{data.head(1)}\n")
|
2750
2762
|
if daysToLookback is None:
|
2751
2763
|
daysToLookback = self.configManager.daysToLookback
|
2752
|
-
volatility = df[
|
2764
|
+
volatility = df["close"].rolling(window=20).std()
|
2753
2765
|
if self.configManager.useEMA:
|
2754
|
-
sma = pktalib.EMA(data["
|
2755
|
-
lma = pktalib.EMA(data["
|
2756
|
-
ssma = pktalib.EMA(data["
|
2757
|
-
ssma20 = pktalib.EMA(data["
|
2766
|
+
sma = pktalib.EMA(data["close"], timeperiod=50)
|
2767
|
+
lma = pktalib.EMA(data["close"], timeperiod=200)
|
2768
|
+
ssma = pktalib.EMA(data["close"], timeperiod=9)
|
2769
|
+
ssma20 = pktalib.EMA(data["close"], timeperiod=20)
|
2758
2770
|
data.insert(len(data.columns), "SMA", sma)
|
2759
2771
|
data.insert(len(data.columns), "LMA", lma)
|
2760
2772
|
data.insert(len(data.columns), "SSMA", ssma)
|
2761
2773
|
data.insert(len(data.columns), "SSMA20", ssma20)
|
2762
2774
|
data.insert(len(data.columns), "Volatility", volatility)
|
2763
2775
|
else:
|
2764
|
-
sma = pktalib.SMA(data["
|
2765
|
-
lma = pktalib.SMA(data["
|
2766
|
-
ssma = pktalib.SMA(data["
|
2767
|
-
ssma20 = pktalib.SMA(data["
|
2776
|
+
sma = pktalib.SMA(data["close"], timeperiod=50)
|
2777
|
+
lma = pktalib.SMA(data["close"], timeperiod=200)
|
2778
|
+
ssma = pktalib.SMA(data["close"], timeperiod=9)
|
2779
|
+
ssma20 = pktalib.SMA(data["close"], timeperiod=20)
|
2768
2780
|
data.insert(len(data.columns), "SMA", sma)
|
2769
2781
|
data.insert(len(data.columns), "LMA", lma)
|
2770
2782
|
data.insert(len(data.columns), "SSMA", ssma)
|
2771
2783
|
data.insert(len(data.columns), "SSMA20", ssma20)
|
2772
2784
|
data.insert(len(data.columns), "Volatility", volatility)
|
2773
|
-
vol = pktalib.SMA(data["
|
2774
|
-
rsi = pktalib.RSI(data["
|
2785
|
+
vol = pktalib.SMA(data["volume"], timeperiod=20)
|
2786
|
+
rsi = pktalib.RSI(data["close"], timeperiod=14)
|
2775
2787
|
data.insert(len(data.columns), "VolMA", vol)
|
2776
2788
|
data.insert(len(data.columns), "RSI", rsi)
|
2777
|
-
cci = pktalib.CCI(data["
|
2789
|
+
cci = pktalib.CCI(data["high"], data["low"], data["close"], timeperiod=14)
|
2778
2790
|
data.insert(len(data.columns), "CCI", cci)
|
2779
2791
|
try:
|
2780
2792
|
fastk, fastd = pktalib.STOCHRSI(
|
2781
|
-
data["
|
2793
|
+
data["close"], timeperiod=14, fastk_period=5, fastd_period=3, fastd_matype=0
|
2782
2794
|
)
|
2783
2795
|
data.insert(len(data.columns), "FASTK", fastk)
|
2784
2796
|
data.insert(len(data.columns), "FASTD", fastd)
|
@@ -2808,8 +2820,8 @@ class ScreeningStatistics:
|
|
2808
2820
|
data = data.fillna(0)
|
2809
2821
|
data = data.replace([np.inf, -np.inf], 0)
|
2810
2822
|
data = data[::-1] # Reverse the dataframe so that its the oldest date first
|
2811
|
-
data["SMA20"] = pktalib.SMA(data["
|
2812
|
-
data["SMA20V"] = pktalib.SMA(data["
|
2823
|
+
data["SMA20"] = pktalib.SMA(data["close"], 20)
|
2824
|
+
data["SMA20V"] = pktalib.SMA(data["volume"], 20)
|
2813
2825
|
data = data[
|
2814
2826
|
::-1
|
2815
2827
|
] # Reverse the dataframe so that it's the most recent date first
|
@@ -2817,13 +2829,13 @@ class ScreeningStatistics:
|
|
2817
2829
|
if len(recent) < 3:
|
2818
2830
|
return False
|
2819
2831
|
# Price at least 1% higher than previous close
|
2820
|
-
cond1 = recent["
|
2832
|
+
cond1 = recent["close"].iloc[0] > 1.01*recent["close"].iloc[1]
|
2821
2833
|
# Volume at least 5% higher than previous volume
|
2822
|
-
cond6 = recent["
|
2823
|
-
cond2 = cond1 and cond6 and (recent["
|
2824
|
-
cond3 = cond2 and (recent["
|
2825
|
-
cond4 = cond3 and (recent["
|
2826
|
-
cond5 = cond4 and (recent["
|
2834
|
+
cond6 = recent["volume"].iloc[0] > 1.05*recent["volume"].iloc[1]
|
2835
|
+
cond2 = cond1 and cond6 and (recent["close"].iloc[0] > recent["SMA20"].iloc[0])
|
2836
|
+
cond3 = cond2 and (recent["close"].iloc[1] > recent["high"].iloc[2])
|
2837
|
+
cond4 = cond3 and (recent["volume"].iloc[0] > 1.05*recent["SMA20V"].iloc[0])
|
2838
|
+
cond5 = cond4 and (recent["volume"].iloc[1] > recent["SMA20V"].iloc[0])
|
2827
2839
|
return cond5
|
2828
2840
|
|
2829
2841
|
def validateBullishForTomorrow(self, df):
|
@@ -2834,9 +2846,9 @@ class ScreeningStatistics:
|
|
2834
2846
|
data = data.fillna(0)
|
2835
2847
|
data = data.replace([np.inf, -np.inf], 0)
|
2836
2848
|
data = data[::-1] # Reverse the dataframe so that its the oldest date first
|
2837
|
-
macdLine = pktalib.MACD(data["
|
2838
|
-
macdSignal = pktalib.MACD(data["
|
2839
|
-
macdHist = pktalib.MACD(data["
|
2849
|
+
macdLine = pktalib.MACD(data["close"], 12, 26, 9)[0].tail(3)
|
2850
|
+
macdSignal = pktalib.MACD(data["close"], 12, 26, 9)[1].tail(3)
|
2851
|
+
macdHist = pktalib.MACD(data["close"], 12, 26, 9)[2].tail(3)
|
2840
2852
|
|
2841
2853
|
return (
|
2842
2854
|
(macdHist.iloc[:1].iloc[0] < macdHist.iloc[:2].iloc[1])
|
@@ -2914,21 +2926,21 @@ class ScreeningStatistics:
|
|
2914
2926
|
while recentCurrentDay <= maxRecentDays:
|
2915
2927
|
# 8 ema>21 ema > 55 ema >200 sma each OF THE ema AND THE 200 sma SEPARATED BY LESS THAN 1%(ideally 0.1% TO 0.5%) DURING CONFLUENCE
|
2916
2928
|
if len(emas) >= 1:
|
2917
|
-
ema_8 = pktalib.EMA(reversedData["
|
2918
|
-
ema_8_prev = pktalib.EMA(reversedData["
|
2929
|
+
ema_8 = pktalib.EMA(reversedData["close"],int(emas[0])).tail(recentCurrentDay).head(1).iloc[0]
|
2930
|
+
ema_8_prev = pktalib.EMA(reversedData["close"],int(emas[0])).tail(recentCurrentDay+1).head(1).iloc[0]
|
2919
2931
|
if len(emas) >= 2:
|
2920
|
-
ema_21 = pktalib.EMA(reversedData["
|
2921
|
-
ema_21_prev = pktalib.EMA(reversedData["
|
2932
|
+
ema_21 = pktalib.EMA(reversedData["close"],int(emas[1])).tail(recentCurrentDay).head(1).iloc[0]
|
2933
|
+
ema_21_prev = pktalib.EMA(reversedData["close"],int(emas[1])).tail(recentCurrentDay+1).head(1).iloc[0]
|
2922
2934
|
if len(emas) >= 3:
|
2923
|
-
ema_55 = pktalib.EMA(reversedData["
|
2924
|
-
ema_55_prev = pktalib.EMA(reversedData["
|
2935
|
+
ema_55 = pktalib.EMA(reversedData["close"],int(emas[2])).tail(recentCurrentDay).head(1).iloc[0]
|
2936
|
+
ema_55_prev = pktalib.EMA(reversedData["close"],int(emas[2])).tail(recentCurrentDay+1).head(1).iloc[0]
|
2925
2937
|
|
2926
2938
|
ema8CrossedEMA21 = (ema_8 >= ema_21 and ema_8_prev <= ema_21_prev) or ema8CrossedEMA21
|
2927
2939
|
ema8CrossedEMA55 = (ema_8 >= ema_55 and ema_8_prev <= ema_55_prev) or ema8CrossedEMA55
|
2928
2940
|
ema21CrossedEMA55 = (ema_21 >= ema_55 and ema_21_prev <= ema_55_prev) or ema21CrossedEMA55
|
2929
2941
|
|
2930
|
-
sma_200 = pktalib.SMA(reversedData["
|
2931
|
-
# ema9 = pktalib.EMA(reversedData["
|
2942
|
+
sma_200 = pktalib.SMA(reversedData["close"],200).tail(recentCurrentDay).head(1).iloc[0]
|
2943
|
+
# ema9 = pktalib.EMA(reversedData["close"],9).tail(recentCurrentDay).head(1).iloc[0]
|
2932
2944
|
# smaRange = sma_200 * percentage
|
2933
2945
|
superConfluenceEnforce200SMA = self.configManager.superConfluenceEnforce200SMA
|
2934
2946
|
# ema_min = min(ema_8, ema_21, ema_55)
|
@@ -2991,10 +3003,10 @@ class ScreeningStatistics:
|
|
2991
3003
|
key4 = "50DMA"
|
2992
3004
|
is50DMAUpTrend = (recent[key1].iloc[0] > recent[key2].iloc[1])
|
2993
3005
|
is50DMADownTrend = (recent[key1].iloc[0] < recent[key1].iloc[1])
|
2994
|
-
is50DMA = (recent[key1].iloc[0] <= recent["
|
2995
|
-
is200DMA = (recent[key2].iloc[0] <= recent["
|
3006
|
+
is50DMA = (recent[key1].iloc[0] <= recent["close"].iloc[0])
|
3007
|
+
is200DMA = (recent[key2].iloc[0] <= recent["close"].iloc[0])
|
2996
3008
|
difference = round((recent[key1].iloc[0] - recent[key2].iloc[0])
|
2997
|
-
/ recent["
|
3009
|
+
/ recent["close"].iloc[0]
|
2998
3010
|
* 100,
|
2999
3011
|
2,
|
3000
3012
|
)
|
@@ -3044,11 +3056,11 @@ class ScreeningStatistics:
|
|
3044
3056
|
return False
|
3045
3057
|
data = df.copy()
|
3046
3058
|
reversedData = data[::-1] # Reverse the dataframe
|
3047
|
-
recentClose = reversedData["
|
3048
|
-
yesterdayClose = reversedData["
|
3049
|
-
recentOpen = reversedData["
|
3050
|
-
yesterdayOpen = reversedData["
|
3051
|
-
recentVol = reversedData["
|
3059
|
+
recentClose = reversedData["close"].tail(1).head(1).iloc[0]
|
3060
|
+
yesterdayClose = reversedData["close"].tail(2).head(1).iloc[0]
|
3061
|
+
recentOpen = reversedData["open"].tail(1).head(1).iloc[0]
|
3062
|
+
yesterdayOpen = reversedData["open"].tail(2).head(1).iloc[0]
|
3063
|
+
recentVol = reversedData["volume"].tail(1).head(1).iloc[0]
|
3052
3064
|
# Daily open > 1 day ago open &
|
3053
3065
|
# Daily Close > 1 day ago close &
|
3054
3066
|
# Volume > 1000000
|
@@ -3070,7 +3082,7 @@ class ScreeningStatistics:
|
|
3070
3082
|
lma_200 = reversedData["LMA"]
|
3071
3083
|
sma_50 = reversedData["SMA"]
|
3072
3084
|
full52Week = reversedData.tail(50 * one_week)
|
3073
|
-
full52WeekLow = full52Week["
|
3085
|
+
full52WeekLow = full52Week["low"].min()
|
3074
3086
|
#200 MA is rising for at least 3 months
|
3075
3087
|
today = 1
|
3076
3088
|
while today <= one_week * 12: # last 3 months
|
@@ -3081,14 +3093,14 @@ class ScreeningStatistics:
|
|
3081
3093
|
if sma_50.tail(1).head(1).iloc[0] <= lma_200.tail(1).head(1).iloc[0]:
|
3082
3094
|
return False
|
3083
3095
|
# Current price is above 20Osma and preferably above 50 to 100
|
3084
|
-
recentClose = reversedData["
|
3096
|
+
recentClose = reversedData["close"].tail(1).head(1).iloc[0]
|
3085
3097
|
if recentClose < lma_200.tail(1).head(1).iloc[0] or recentClose < 50 or recentClose > 100:
|
3086
3098
|
return False
|
3087
3099
|
# Current price is at least above 100 % from 52week low
|
3088
3100
|
if recentClose <= 2*full52WeekLow:
|
3089
3101
|
return False
|
3090
3102
|
# The stock should have made a 52 week high at least once every 4 to 6 month
|
3091
|
-
highAsc = reversedData.sort_values(by=["
|
3103
|
+
highAsc = reversedData.sort_values(by=["high"], ascending=True)
|
3092
3104
|
highs = highAsc.tail(13)
|
3093
3105
|
dateDiffs = highs.index.to_series().diff().dt.days
|
3094
3106
|
index = 0
|
@@ -3107,29 +3119,29 @@ class ScreeningStatistics:
|
|
3107
3119
|
return False
|
3108
3120
|
data = full_df.copy()
|
3109
3121
|
reversedData = data[::-1] # Reverse the dataframe
|
3110
|
-
recentClose = reversedData["
|
3111
|
-
prevClose = reversedData["
|
3122
|
+
recentClose = reversedData["close"].tail(1).head(1).iloc[0]
|
3123
|
+
prevClose = reversedData["close"].tail(2).head(1).iloc[0]
|
3112
3124
|
tradingAbove2Percent = (recentClose-prevClose)*100/prevClose > 2
|
3113
3125
|
if tradingAbove2Percent:
|
3114
|
-
prevHigh = reversedData["
|
3126
|
+
prevHigh = reversedData["high"].tail(2).head(1).iloc[0]
|
3115
3127
|
tradingAbovePrevHighAnd50MA = (recentClose > prevHigh) and (recentClose > reversedData["SMA"].tail(1).head(1).iloc[0])
|
3116
3128
|
# return tradingAbovePrevHighAnd50MA
|
3117
3129
|
# resampling 1-min data to 5 min for 200MA requires at least 5d data to
|
3118
3130
|
# be downloaded which is pretty huge (~460MB). So skipping this for now.
|
3119
3131
|
if tradingAbovePrevHighAnd50MA:
|
3120
3132
|
ohlc_dict = {
|
3121
|
-
|
3122
|
-
|
3123
|
-
|
3124
|
-
|
3133
|
+
"open":'first',
|
3134
|
+
"high":'max',
|
3135
|
+
"low":'min',
|
3136
|
+
"close":'last',
|
3125
3137
|
'Adj Close': 'last',
|
3126
|
-
|
3138
|
+
"volume":'sum'
|
3127
3139
|
}
|
3128
3140
|
data_5min = df_5min.copy()
|
3129
3141
|
reversedData_5min = data_5min[::-1] # Reverse the dataframe
|
3130
3142
|
reversedData_5min = reversedData_5min.resample(f'5T', offset='15min').agg(ohlc_dict)
|
3131
3143
|
reversedData_5min.dropna(inplace=True)
|
3132
|
-
sma200_5min = pktalib.SMA(reversedData_5min["
|
3144
|
+
sma200_5min = pktalib.SMA(reversedData_5min["close"],timeperiod=200)
|
3133
3145
|
return recentClose > sma200_5min.tail(1).head(1).iloc[0]
|
3134
3146
|
return False
|
3135
3147
|
|
@@ -3141,8 +3153,8 @@ class ScreeningStatistics:
|
|
3141
3153
|
data = df.copy()
|
3142
3154
|
data = data.fillna(0)
|
3143
3155
|
data = data.replace([np.inf, -np.inf], 0)
|
3144
|
-
hc = data.describe()["
|
3145
|
-
lc = data.describe()["
|
3156
|
+
hc = data.describe()["close"]["max"]
|
3157
|
+
lc = data.describe()["close"]["min"]
|
3146
3158
|
if (hc - lc) <= (hc * percentage / 100) and (hc - lc != 0):
|
3147
3159
|
screenDict["Consol."] = (
|
3148
3160
|
colorText.GREEN
|
@@ -3249,19 +3261,19 @@ class ScreeningStatistics:
|
|
3249
3261
|
if len(day1) < 1 or len(day2) < 1 or len(day3) < 1:
|
3250
3262
|
return False
|
3251
3263
|
higherHighs = (
|
3252
|
-
(day0["
|
3253
|
-
and (day1["
|
3254
|
-
and (day2["
|
3264
|
+
(day0["high"].iloc[0] > day1["high"].iloc[0])
|
3265
|
+
and (day1["high"].iloc[0] > day2["high"].iloc[0])
|
3266
|
+
and (day2["high"].iloc[0] > day3["high"].iloc[0])
|
3255
3267
|
)
|
3256
3268
|
higherLows = (
|
3257
|
-
(day0["
|
3258
|
-
and (day1["
|
3259
|
-
and (day2["
|
3269
|
+
(day0["low"].iloc[0] > day1["low"].iloc[0])
|
3270
|
+
and (day1["low"].iloc[0] > day2["low"].iloc[0])
|
3271
|
+
and (day2["low"].iloc[0] > day3["low"].iloc[0])
|
3260
3272
|
)
|
3261
3273
|
higherClose = (
|
3262
|
-
(day0["
|
3263
|
-
and (day1["
|
3264
|
-
and (day2["
|
3274
|
+
(day0["close"].iloc[0] > day1["close"].iloc[0])
|
3275
|
+
and (day1["close"].iloc[0] > day2["close"].iloc[0])
|
3276
|
+
and (day2["close"].iloc[0] > day3["close"].iloc[0])
|
3265
3277
|
)
|
3266
3278
|
# higherRSI = (day0["RSI"].iloc[0] > day1["RSI"].iloc[0]) and \
|
3267
3279
|
# (day1["RSI"].iloc[0] > day2["RSI"].iloc[0]) and \
|
@@ -3269,11 +3281,11 @@ class ScreeningStatistics:
|
|
3269
3281
|
# day3["RSI"].iloc[0] >= 50 and day0["RSI"].iloc[0] >= 65
|
3270
3282
|
reversedData = data[::-1].copy()
|
3271
3283
|
reversedData["SUPERT"] = pktalib.supertrend(reversedData, 7, 3)["SUPERT_7_3.0"]
|
3272
|
-
reversedData["EMA8"] = pktalib.EMA(reversedData["
|
3284
|
+
reversedData["EMA8"] = pktalib.EMA(reversedData["close"], timeperiod=9)
|
3273
3285
|
higherClose = (
|
3274
3286
|
higherClose
|
3275
|
-
and day0["
|
3276
|
-
and day0["
|
3287
|
+
and day0["close"].iloc[0] > reversedData.tail(1)["SUPERT"].iloc[0]
|
3288
|
+
and day0["close"].iloc[0] > reversedData.tail(1)["EMA8"].iloc[0]
|
3277
3289
|
)
|
3278
3290
|
return higherHighs and higherLows and higherClose
|
3279
3291
|
|
@@ -3298,10 +3310,10 @@ class ScreeningStatistics:
|
|
3298
3310
|
data = orgData.head(i)
|
3299
3311
|
refCandle = data.tail(1)
|
3300
3312
|
if (
|
3301
|
-
(len(data.
|
3302
|
-
and (len(data.
|
3303
|
-
and (len(data.
|
3304
|
-
and (len(data.
|
3313
|
+
(len(data.high[data.high > refCandle.high.item()]) == 0)
|
3314
|
+
and (len(data.low[data.low < refCandle.low.item()]) == 0)
|
3315
|
+
and (len(data.open[data.open > refCandle.high.item()]) == 0)
|
3316
|
+
and (len(data.close[data.close < refCandle.low.item()]) == 0)
|
3305
3317
|
):
|
3306
3318
|
screenDict["Pattern"] = (
|
3307
3319
|
saved[0]
|
@@ -3320,10 +3332,10 @@ class ScreeningStatistics:
|
|
3320
3332
|
data = orgData.head(i)
|
3321
3333
|
refCandle = data.tail(1)
|
3322
3334
|
if (
|
3323
|
-
(len(data.
|
3324
|
-
and (len(data.
|
3325
|
-
and (len(data.
|
3326
|
-
and (len(data.
|
3335
|
+
(len(data.high[data.high > refCandle.high.item()]) == 0)
|
3336
|
+
and (len(data.low[data.low < refCandle.low.item()]) == 0)
|
3337
|
+
and (len(data.open[data.open > refCandle.high.item()]) == 0)
|
3338
|
+
and (len(data.close[data.close < refCandle.low.item()]) == 0)
|
3327
3339
|
):
|
3328
3340
|
screenDict["Pattern"] = (
|
3329
3341
|
saved[0]
|
@@ -3342,9 +3354,9 @@ class ScreeningStatistics:
|
|
3342
3354
|
if df is None or len(df) == 0:
|
3343
3355
|
return False
|
3344
3356
|
data = df.copy()
|
3345
|
-
listingPrice = data[::-1].head(1)["
|
3346
|
-
currentPrice = data.head(1)["
|
3347
|
-
ATH = data.describe()["
|
3357
|
+
listingPrice = data[::-1].head(1)["open"].iloc[0]
|
3358
|
+
currentPrice = data.head(1)["close"].iloc[0]
|
3359
|
+
ATH = data.describe()["high"]["max"]
|
3348
3360
|
if ATH > (listingPrice + (listingPrice * percentage)):
|
3349
3361
|
return False
|
3350
3362
|
away = round(((currentPrice - listingPrice) / listingPrice) * 100, 1)
|
@@ -3384,11 +3396,11 @@ class ScreeningStatistics:
|
|
3384
3396
|
data = data[::-1] # Reverse the dataframe
|
3385
3397
|
data = data.rename(
|
3386
3398
|
columns={
|
3387
|
-
"
|
3388
|
-
"
|
3389
|
-
"
|
3390
|
-
"
|
3391
|
-
"
|
3399
|
+
"open": "open",
|
3400
|
+
"close": "close",
|
3401
|
+
"high": "high",
|
3402
|
+
"low": "low",
|
3403
|
+
"volume": "volume",
|
3392
3404
|
}
|
3393
3405
|
)
|
3394
3406
|
try:
|
@@ -3400,10 +3412,10 @@ class ScreeningStatistics:
|
|
3400
3412
|
ata.LorentzianClassification.Feature("CCI", 20, 2), # f3
|
3401
3413
|
ata.LorentzianClassification.Feature("ADX", 20, 2), # f4
|
3402
3414
|
ata.LorentzianClassification.Feature("RSI", 9, 2), # f5
|
3403
|
-
pktalib.MFI(data[
|
3415
|
+
pktalib.MFI(data["high"], data["low"], data["close"], data["volume"], 14) #f6
|
3404
3416
|
],
|
3405
3417
|
settings=ata.LorentzianClassification.Settings(
|
3406
|
-
source=data[
|
3418
|
+
source=data["close"],
|
3407
3419
|
neighborsCount=8,
|
3408
3420
|
maxBarsBack=2000,
|
3409
3421
|
useDynamicExits=False
|
@@ -3465,14 +3477,14 @@ class ScreeningStatistics:
|
|
3465
3477
|
day2 = data[2:]
|
3466
3478
|
day3 = data[3:]
|
3467
3479
|
lowerHighs = (
|
3468
|
-
(day0["
|
3469
|
-
and (day1["
|
3470
|
-
and (day2["
|
3480
|
+
(day0["high"].iloc[0] < day1["high"].iloc[0])
|
3481
|
+
and (day1["high"].iloc[0] < day2["high"].iloc[0])
|
3482
|
+
and (day2["high"].iloc[0] < day3["high"].iloc[0])
|
3471
3483
|
)
|
3472
3484
|
lowerLows = (
|
3473
|
-
(day0["
|
3474
|
-
and (day1["
|
3475
|
-
and (day2["
|
3485
|
+
(day0["low"].iloc[0] < day1["low"].iloc[0])
|
3486
|
+
and (day1["low"].iloc[0] < day2["low"].iloc[0])
|
3487
|
+
and (day2["low"].iloc[0] < day3["low"].iloc[0])
|
3476
3488
|
)
|
3477
3489
|
higherRSI = (
|
3478
3490
|
(day0["RSI"].iloc[0] < day1["RSI"].iloc[0])
|
@@ -3497,8 +3509,8 @@ class ScreeningStatistics:
|
|
3497
3509
|
recent = data.head(1)
|
3498
3510
|
if len(recent) < 1:
|
3499
3511
|
return False
|
3500
|
-
if (recent["
|
3501
|
-
"
|
3512
|
+
if (recent["volume"].iloc[0] <= data.describe()["volume"]["min"]) and recent[
|
3513
|
+
"volume"
|
3502
3514
|
][0] != np.nan:
|
3503
3515
|
return True
|
3504
3516
|
return False
|
@@ -3515,7 +3527,7 @@ class ScreeningStatistics:
|
|
3515
3527
|
data = data.replace([np.inf, -np.inf], 0)
|
3516
3528
|
recent = data.head(1)
|
3517
3529
|
|
3518
|
-
pct_change = (data[::-1]["
|
3530
|
+
pct_change = (data[::-1]["close"].pct_change() * 100).iloc[-1]
|
3519
3531
|
if pct_change == np.inf or pct_change == -np.inf:
|
3520
3532
|
pct_change = 0
|
3521
3533
|
pct_save = "%.1f%%" % pct_change
|
@@ -3527,11 +3539,11 @@ class ScreeningStatistics:
|
|
3527
3539
|
pct_change = colorText.WARN + ("%.1f%%" % pct_change) + colorText.END
|
3528
3540
|
saveDict["%Chng"] = pct_save
|
3529
3541
|
screenDict["%Chng"] = pct_change
|
3530
|
-
ltp = round(recent["
|
3542
|
+
ltp = round(recent["close"].iloc[0], 2)
|
3531
3543
|
verifyStageTwo = True
|
3532
3544
|
if len(data) > 250:
|
3533
|
-
yearlyLow = data.head(250)["
|
3534
|
-
yearlyHigh = data.head(250)["
|
3545
|
+
yearlyLow = data.head(250)["close"].min()
|
3546
|
+
yearlyHigh = data.head(250)["close"].max()
|
3535
3547
|
if ltp < (2 * yearlyLow) and ltp < (0.75 * yearlyHigh):
|
3536
3548
|
verifyStageTwo = False
|
3537
3549
|
screenDict["Stock"] = colorText.FAIL + saveDict["Stock"] + colorText.END
|
@@ -3578,8 +3590,8 @@ class ScreeningStatistics:
|
|
3578
3590
|
calc_date = str(previous_recent.iloc[:, 0][0]).split(" ")[0]
|
3579
3591
|
for prd in periods:
|
3580
3592
|
if len(data) >= prd + 1:
|
3581
|
-
prevLtp = data["
|
3582
|
-
ltpTdy = data["
|
3593
|
+
prevLtp = data["close"].iloc[0]
|
3594
|
+
ltpTdy = data["close"].iloc[prd]
|
3583
3595
|
if isinstance(prevLtp,pd.Series):
|
3584
3596
|
prevLtp = prevLtp[0]
|
3585
3597
|
ltpTdy = ltpTdy[0]
|
@@ -3600,7 +3612,7 @@ class ScreeningStatistics:
|
|
3600
3612
|
saveDict[f"{prd}-Pd"] = f"{changePercent}%" if not pd.isna(changePercent) else '-'
|
3601
3613
|
screenDict[f"{prd}-Pd"] = ((colorText.GREEN if changePercent >=0 else colorText.FAIL) + f"{changePercent}%" + colorText.END) if not pd.isna(changePercent) else '-'
|
3602
3614
|
if (prd == requestedPeriod):
|
3603
|
-
maxLTPPotential = max(data["
|
3615
|
+
maxLTPPotential = max(data["high"].head(prd))
|
3604
3616
|
screenDict[f"MaxLTP"] = (
|
3605
3617
|
(colorText.GREEN if (maxLTPPotential >= prevLtp) else (colorText.FAIL))
|
3606
3618
|
+ str("{:.2f}".format(maxLTPPotential))
|
@@ -3629,7 +3641,7 @@ class ScreeningStatistics:
|
|
3629
3641
|
data = data.fillna(0)
|
3630
3642
|
data = data.replace([np.inf, -np.inf], 0)
|
3631
3643
|
data = data[::-1] # Reverse the dataframe so that its the oldest date first
|
3632
|
-
macd = pktalib.MACD(data["
|
3644
|
+
macd = pktalib.MACD(data["close"], 12, 26, 9)[2].tail(1)
|
3633
3645
|
return macd.iloc[:1][0] < 0
|
3634
3646
|
|
3635
3647
|
#@measure_time
|
@@ -3644,16 +3656,16 @@ class ScreeningStatistics:
|
|
3644
3656
|
return False
|
3645
3657
|
for row in data.iterrows():
|
3646
3658
|
# All 3 candles should be Green and NOT Circuits
|
3647
|
-
yc = row[1]["
|
3648
|
-
yo = row[1]["
|
3659
|
+
yc = row[1]["close"]
|
3660
|
+
yo = row[1]["open"]
|
3649
3661
|
if yc <= yo:
|
3650
3662
|
# self.default_logger.info(
|
3651
3663
|
# f'Stock:{saveDict["Stock"]}, is not a momentum-gainer because yesterday-close ({yc}) <= yesterday-open ({yo})'
|
3652
3664
|
# )
|
3653
3665
|
return False
|
3654
|
-
openDesc = data.sort_values(by=["
|
3655
|
-
closeDesc = data.sort_values(by=["
|
3656
|
-
volDesc = data.sort_values(by=["
|
3666
|
+
openDesc = data.sort_values(by=["open"], ascending=False)
|
3667
|
+
closeDesc = data.sort_values(by=["close"], ascending=False)
|
3668
|
+
volDesc = data.sort_values(by=["volume"], ascending=False)
|
3657
3669
|
try:
|
3658
3670
|
if (
|
3659
3671
|
data.equals(openDesc)
|
@@ -3663,10 +3675,10 @@ class ScreeningStatistics:
|
|
3663
3675
|
# self.default_logger.info(
|
3664
3676
|
# f'Stock:{saveDict["Stock"]}, open,close and volume equal from day before yesterday. A potential momentum-gainer!'
|
3665
3677
|
# )
|
3666
|
-
to = data["
|
3667
|
-
yc = data["
|
3668
|
-
yo = data["
|
3669
|
-
dyc = data["
|
3678
|
+
to = data["open"].iloc[0]
|
3679
|
+
yc = data["close"].iloc[1]
|
3680
|
+
yo = data["open"].iloc[1]
|
3681
|
+
dyc = data["close"].iloc[2]
|
3670
3682
|
if (to >= yc) and (yo >= dyc):
|
3671
3683
|
# self.default_logger.info(
|
3672
3684
|
# f'Stock:{saveDict["Stock"]}, is a momentum-gainer because today-open ({to}) >= yesterday-close ({yc}) and yesterday-open({yo}) >= day-before-close({dyc})'
|
@@ -3706,7 +3718,7 @@ class ScreeningStatistics:
|
|
3706
3718
|
saved = self.findCurrentSavedValue(screenDict,saveDict,"MA-Signal")
|
3707
3719
|
if (
|
3708
3720
|
recent["SMA"].iloc[0] > recent["LMA"].iloc[0]
|
3709
|
-
and recent["
|
3721
|
+
and recent["close"].iloc[0] > recent["SMA"].iloc[0]
|
3710
3722
|
):
|
3711
3723
|
screenDict["MA-Signal"] = (
|
3712
3724
|
saved[0] + colorText.GREEN + "Bullish" + colorText.END
|
@@ -3730,17 +3742,17 @@ class ScreeningStatistics:
|
|
3730
3742
|
)
|
3731
3743
|
saveDict["MA-Signal"] = saved[1] + "Neutral"
|
3732
3744
|
reversedData = data[::-1] # Reverse the dataframe
|
3733
|
-
ema_20 = pktalib.EMA(reversedData["
|
3734
|
-
vwap = pktalib.VWAP(reversedData["
|
3745
|
+
ema_20 = pktalib.EMA(reversedData["close"],20).tail(1).iloc[0]
|
3746
|
+
vwap = pktalib.VWAP(reversedData["high"],reversedData["low"],reversedData["close"],reversedData["volume"]).tail(1).iloc[0]
|
3735
3747
|
smaDev = data["SMA"].iloc[0] * maRange / 100
|
3736
3748
|
lmaDev = data["LMA"].iloc[0] * maRange / 100
|
3737
3749
|
emaDev = ema_20 * maRange / 100
|
3738
3750
|
vwapDev = vwap * maRange / 100
|
3739
3751
|
open, high, low, close, sma, lma = (
|
3740
|
-
data["
|
3741
|
-
data["
|
3742
|
-
data["
|
3743
|
-
data["
|
3752
|
+
data["open"].iloc[0],
|
3753
|
+
data["high"].iloc[0],
|
3754
|
+
data["low"].iloc[0],
|
3755
|
+
data["close"].iloc[0],
|
3744
3756
|
data["SMA"].iloc[0],
|
3745
3757
|
data["LMA"].iloc[0],
|
3746
3758
|
)
|
@@ -3810,7 +3822,7 @@ class ScreeningStatistics:
|
|
3810
3822
|
if PKDateUtilities.isTradingTime():
|
3811
3823
|
rangeData = data.head(nr + 1)[1:]
|
3812
3824
|
now_candle = data.head(1)
|
3813
|
-
rangeData["Range"] = abs(rangeData["
|
3825
|
+
rangeData["Range"] = abs(rangeData["close"] - rangeData["open"])
|
3814
3826
|
recent = rangeData.head(1)
|
3815
3827
|
if (
|
3816
3828
|
len(recent) == 1
|
@@ -3818,7 +3830,7 @@ class ScreeningStatistics:
|
|
3818
3830
|
):
|
3819
3831
|
if (
|
3820
3832
|
self.getCandleType(recent)
|
3821
|
-
and now_candle["
|
3833
|
+
and now_candle["close"].iloc[0] >= recent["close"].iloc[0]
|
3822
3834
|
):
|
3823
3835
|
screenDict["Pattern"] = (
|
3824
3836
|
saved[0] + colorText.GREEN + f"Buy-NR{nr}" + colorText.END
|
@@ -3827,7 +3839,7 @@ class ScreeningStatistics:
|
|
3827
3839
|
return True
|
3828
3840
|
elif (
|
3829
3841
|
not self.getCandleType(recent)
|
3830
|
-
and now_candle["
|
3842
|
+
and now_candle["close"].iloc[0] <= recent["close"].iloc[0]
|
3831
3843
|
):
|
3832
3844
|
screenDict["Pattern"] = (
|
3833
3845
|
saved[0] + colorText.FAIL + f"Sell-NR{nr}" + colorText.END
|
@@ -3837,7 +3849,7 @@ class ScreeningStatistics:
|
|
3837
3849
|
return False
|
3838
3850
|
else:
|
3839
3851
|
rangeData = data.head(nr)
|
3840
|
-
rangeData.loc[:,'Range'] = abs(rangeData["
|
3852
|
+
rangeData.loc[:,'Range'] = abs(rangeData["close"] - rangeData["open"])
|
3841
3853
|
recent = rangeData.head(1)
|
3842
3854
|
if recent["Range"].iloc[0] == rangeData.describe()["Range"]["min"]:
|
3843
3855
|
screenDict["Pattern"] = (
|
@@ -3859,7 +3871,7 @@ class ScreeningStatistics:
|
|
3859
3871
|
if len(recent) < 1:
|
3860
3872
|
return False
|
3861
3873
|
if len(data) < daysToLookback and (
|
3862
|
-
recent["
|
3874
|
+
recent["close"].iloc[0] != np.nan and recent["close"].iloc[0] > 0
|
3863
3875
|
):
|
3864
3876
|
return True
|
3865
3877
|
return False
|
@@ -3873,7 +3885,7 @@ class ScreeningStatistics:
|
|
3873
3885
|
for ma in mas:
|
3874
3886
|
if len(reversedData) <= int(ma):
|
3875
3887
|
continue
|
3876
|
-
hasCrossed, percentageDiff = self.findPriceActionCross(df=reversedData,ma=ma,daysToConsider=1,baseMAOrPrice=reversedData["
|
3888
|
+
hasCrossed, percentageDiff = self.findPriceActionCross(df=reversedData,ma=ma,daysToConsider=1,baseMAOrPrice=reversedData["close"].tail(2),isEMA=isEMA,maDirectionFromBelow=maDirectionFromBelow)
|
3877
3889
|
if hasCrossed:
|
3878
3890
|
if not hasAtleastOneMACross:
|
3879
3891
|
hasAtleastOneMACross = True
|
@@ -3891,17 +3903,17 @@ class ScreeningStatistics:
|
|
3891
3903
|
pp_map = {"1":"PP","2":"S1","3":"S2","4":"S3","5":"R1","6":"R2","7":"R3"}
|
3892
3904
|
if pivotPoint is not None and pivotPoint != "0" and str(pivotPoint).isnumeric():
|
3893
3905
|
ppToCheck = pp_map[str(pivotPoint)]
|
3894
|
-
ppsr_df = pktalib.get_ppsr_df(data["
|
3906
|
+
ppsr_df = pktalib.get_ppsr_df(data["high"],data["low"],data["close"],ppToCheck)
|
3895
3907
|
if ppsr_df is None:
|
3896
3908
|
return False
|
3897
3909
|
if crossDirectionFromBelow:
|
3898
|
-
hasPriceCross = (ppsr_df["
|
3899
|
-
ppsr_df["
|
3910
|
+
hasPriceCross = (ppsr_df["close"].iloc[0] > ppsr_df[ppToCheck].iloc[0] and
|
3911
|
+
ppsr_df["close"].iloc[1] <= ppsr_df[ppToCheck].iloc[1])
|
3900
3912
|
else:
|
3901
|
-
hasPriceCross = (ppsr_df["
|
3902
|
-
ppsr_df["
|
3913
|
+
hasPriceCross = (ppsr_df["close"].iloc[0] < ppsr_df[ppToCheck].iloc[0] and
|
3914
|
+
ppsr_df["close"].iloc[1] >= ppsr_df[ppToCheck].iloc[1])
|
3903
3915
|
if hasPriceCross:
|
3904
|
-
percentageDiff = round(100*(ppsr_df["
|
3916
|
+
percentageDiff = round(100*(ppsr_df["close"].iloc[0]-ppsr_df[ppToCheck].iloc[0])/ppsr_df[ppToCheck].iloc[0],1)
|
3905
3917
|
saved = self.findCurrentSavedValue(screenDict,saveDict,"MA-Signal")
|
3906
3918
|
maText = f"Cross-{'FromBelow' if crossDirectionFromBelow else 'FromAbove'}({ppToCheck}:{ppsr_df[ppToCheck].iloc[0]})"
|
3907
3919
|
saveDict["MA-Signal"] = saved[1] + maText + f"({percentageDiff}%)"
|
@@ -3918,10 +3930,10 @@ class ScreeningStatistics:
|
|
3918
3930
|
data = data.head(4)
|
3919
3931
|
if len(data) < 4:
|
3920
3932
|
return False
|
3921
|
-
day0 = data.iloc[0]["
|
3922
|
-
dayMinus1 = data.iloc[1]["
|
3923
|
-
dayMinus2 = data.iloc[2]["
|
3924
|
-
dayMinus3 = data.iloc[3]["
|
3933
|
+
day0 = data.iloc[0]["close"].item()
|
3934
|
+
dayMinus1 = data.iloc[1]["close"].item()
|
3935
|
+
dayMinus2 = data.iloc[2]["close"].item()
|
3936
|
+
dayMinus3 = data.iloc[3]["close"].item()
|
3925
3937
|
percent3 = round((dayMinus2 - dayMinus3) * 100 / dayMinus3, 2)
|
3926
3938
|
percent2 = round((dayMinus1 - dayMinus2) * 100 / dayMinus2, 2)
|
3927
3939
|
percent1 = round((day0 - dayMinus1) * 100 / dayMinus1, 2)
|
@@ -3974,11 +3986,11 @@ class ScreeningStatistics:
|
|
3974
3986
|
try:
|
3975
3987
|
df_ichi = df_new.rename(
|
3976
3988
|
columns={
|
3977
|
-
"
|
3978
|
-
"
|
3979
|
-
"
|
3980
|
-
"
|
3981
|
-
"
|
3989
|
+
"open": "open",
|
3990
|
+
"high": "high",
|
3991
|
+
"low": "low",
|
3992
|
+
"close": "close",
|
3993
|
+
"volume": "volume",
|
3982
3994
|
}
|
3983
3995
|
)
|
3984
3996
|
ichi = pktalib.ichimoku(df_ichi, 9, 26, 52, 26)
|
@@ -4000,12 +4012,12 @@ class ScreeningStatistics:
|
|
4000
4012
|
if df_new["cloud_green"].iloc[0]:
|
4001
4013
|
aboveCloudTop = (
|
4002
4014
|
df_new["IKS_26"].iloc[0] > df_new["ISA_9"].iloc[0]
|
4003
|
-
and recent["
|
4015
|
+
and recent["close"].iloc[0] > df_new["ISA_9"].iloc[0]
|
4004
4016
|
)
|
4005
4017
|
elif df_new["cloud_red"].iloc[0]:
|
4006
4018
|
aboveCloudTop = (
|
4007
4019
|
df_new["IKS_26"].iloc[0] > df_new["ISB_26"].iloc[0]
|
4008
|
-
and recent["
|
4020
|
+
and recent["close"].iloc[0] > df_new["ISB_26"].iloc[0]
|
4009
4021
|
)
|
4010
4022
|
|
4011
4023
|
# Latest Ichimoku baseline is < latest Ichimoku conversion line
|
@@ -4020,8 +4032,8 @@ class ScreeningStatistics:
|
|
4020
4032
|
# close > 50 period SMA/EMA and 200 period SMA/EMA
|
4021
4033
|
if (
|
4022
4034
|
recent["SSMA"].iloc[0] > recent["SMA"].iloc[0]
|
4023
|
-
and recent["
|
4024
|
-
and recent["
|
4035
|
+
and recent["close"].iloc[0] > recent["SSMA"].iloc[0]
|
4036
|
+
and recent["close"].iloc[0] > recent["LMA"].iloc[0]
|
4025
4037
|
):
|
4026
4038
|
saved = self.findCurrentSavedValue(screenDict,saveDict,"MA-Signal")
|
4027
4039
|
screenDict["MA-Signal"] = (
|
@@ -4041,22 +4053,22 @@ class ScreeningStatistics:
|
|
4041
4053
|
try:
|
4042
4054
|
if self.configManager.enableAdditionalVCPEMAFilters:
|
4043
4055
|
reversedData = data[::-1]
|
4044
|
-
ema = pktalib.EMA(reversedData["
|
4045
|
-
sema20 = pktalib.EMA(reversedData["
|
4046
|
-
if not (data["
|
4056
|
+
ema = pktalib.EMA(reversedData["close"], timeperiod=50)
|
4057
|
+
sema20 = pktalib.EMA(reversedData["close"], timeperiod=20)
|
4058
|
+
if not (data["close"].iloc[0] >= ema.tail(1).iloc[0] and data["close"].iloc[0] >= sema20.tail(1).iloc[0]):
|
4047
4059
|
return False
|
4048
4060
|
percentageFromTop /= 100
|
4049
4061
|
data.reset_index(inplace=True)
|
4050
4062
|
data.rename(columns={"index": "Date"}, inplace=True)
|
4051
|
-
data["tops"] = (data["
|
4052
|
-
data["bots"] = (data["
|
4063
|
+
data["tops"] = (data["high"].iloc[list(pktalib.argrelextrema(np.array(data["high"]), np.greater_equal, order=window)[0])].head(4))
|
4064
|
+
data["bots"] = (data["low"].iloc[list(pktalib.argrelextrema(np.array(data["low"]), np.less_equal, order=window)[0])].head(4))
|
4053
4065
|
data = data.fillna(0)
|
4054
4066
|
data = data.replace([np.inf, -np.inf], 0)
|
4055
4067
|
tops = data[data.tops > 0]
|
4056
4068
|
# bots = data[data.bots > 0]
|
4057
|
-
highestTop = round(tops.describe()["
|
4058
|
-
allTimeHigh = max(data["
|
4059
|
-
withinATHRange = data["
|
4069
|
+
highestTop = round(tops.describe()["high"]["max"], 1)
|
4070
|
+
allTimeHigh = max(data["high"])
|
4071
|
+
withinATHRange = data["close"].iloc[0] >= (allTimeHigh-allTimeHigh * float(self.configManager.vcpRangePercentageFromTop)/100)
|
4060
4072
|
if not withinATHRange and self.configManager.enableAdditionalVCPFilters:
|
4061
4073
|
# Last close is not within all time high range
|
4062
4074
|
return False
|
@@ -4071,14 +4083,14 @@ class ScreeningStatistics:
|
|
4071
4083
|
lowPoints.append(
|
4072
4084
|
data[
|
4073
4085
|
(data.Date >= startDate) & (data.Date <= endDate)
|
4074
|
-
].describe()["
|
4086
|
+
].describe()["low"]["min"]
|
4075
4087
|
)
|
4076
4088
|
lowPointsOrg = lowPoints
|
4077
4089
|
lowPoints.sort(reverse=True)
|
4078
4090
|
lowPointsSorted = lowPoints
|
4079
4091
|
if data.empty or len(lowPoints) < 1:
|
4080
4092
|
return False
|
4081
|
-
ltp = data.head(1)["
|
4093
|
+
ltp = data.head(1)["close"].iloc[0]
|
4082
4094
|
if (
|
4083
4095
|
lowPointsOrg == lowPointsSorted
|
4084
4096
|
and ltp < highestTop
|
@@ -4112,41 +4124,41 @@ class ScreeningStatistics:
|
|
4112
4124
|
return False
|
4113
4125
|
data = df.copy()
|
4114
4126
|
ohlc_dict = {
|
4115
|
-
|
4116
|
-
|
4117
|
-
|
4118
|
-
|
4119
|
-
|
4127
|
+
"open":'first',
|
4128
|
+
"high":'max',
|
4129
|
+
"low":'min',
|
4130
|
+
"close":'last',
|
4131
|
+
"volume":'sum'
|
4120
4132
|
}
|
4121
4133
|
# final_df = df.resample('W-FRI', closed='left').agg(ohlc_dict).shift('1d')
|
4122
4134
|
weeklyData = data.resample('W').agg(ohlc_dict)
|
4123
4135
|
reversedData = data[::-1] # Reverse the dataframe
|
4124
|
-
recent_close = data["
|
4125
|
-
w_ema_13 = pktalib.EMA(weeklyData["
|
4126
|
-
w_ema_26 = pktalib.EMA(weeklyData["
|
4127
|
-
w_sma_50 = pktalib.SMA(weeklyData["
|
4128
|
-
w_sma_40 = pktalib.SMA(weeklyData["
|
4129
|
-
w_sma_40_5w_ago = pktalib.SMA(weeklyData.head(len(weeklyData)-5)["
|
4130
|
-
w_min_50 = min(1.3*weeklyData.tail(50)["
|
4131
|
-
w_max_50 = max(0.75*weeklyData.tail(50)["
|
4132
|
-
w_ema_26_20w_ago = pktalib.EMA(weeklyData.head(len(weeklyData)-20)["
|
4133
|
-
recent_ema_13_20d_ago = pktalib.EMA(reversedData.head(len(reversedData)-20)["
|
4134
|
-
w_sma_40_5w_ago = pktalib.SMA(weeklyData.head(len(weeklyData)-5)["
|
4135
|
-
w_sma_40_10w_ago = pktalib.SMA(weeklyData.head(len(weeklyData)-10)["
|
4136
|
-
recent_sma_50 = pktalib.SMA(reversedData["
|
4137
|
-
w_wma_8 = pktalib.WMA(weeklyData["
|
4138
|
-
w_sma_8 = pktalib.SMA(weeklyData["
|
4136
|
+
recent_close = data["close"].head(1).iloc[0]
|
4137
|
+
w_ema_13 = pktalib.EMA(weeklyData["close"],timeperiod=13).tail(1).iloc[0]
|
4138
|
+
w_ema_26 = pktalib.EMA(weeklyData["close"],timeperiod=26).tail(1).iloc[0]
|
4139
|
+
w_sma_50 = pktalib.SMA(weeklyData["close"],timeperiod=50).tail(1).iloc[0]
|
4140
|
+
w_sma_40 = pktalib.SMA(weeklyData["close"],timeperiod=40).tail(1).iloc[0]
|
4141
|
+
w_sma_40_5w_ago = pktalib.SMA(weeklyData.head(len(weeklyData)-5)["close"],timeperiod=40).tail(1).iloc[0]
|
4142
|
+
w_min_50 = min(1.3*weeklyData.tail(50)["low"])
|
4143
|
+
w_max_50 = max(0.75*weeklyData.tail(50)["high"])
|
4144
|
+
w_ema_26_20w_ago = pktalib.EMA(weeklyData.head(len(weeklyData)-20)["close"],timeperiod=26).tail(1).iloc[0]
|
4145
|
+
recent_ema_13_20d_ago = pktalib.EMA(reversedData.head(len(reversedData)-20)["close"],timeperiod=13).tail(1).iloc[0]
|
4146
|
+
w_sma_40_5w_ago = pktalib.SMA(weeklyData.head(len(weeklyData)-5)["close"],timeperiod=40).tail(1).iloc[0]
|
4147
|
+
w_sma_40_10w_ago = pktalib.SMA(weeklyData.head(len(weeklyData)-10)["close"],timeperiod=40).tail(1).iloc[0]
|
4148
|
+
recent_sma_50 = pktalib.SMA(reversedData["close"],timeperiod=50).tail(1).iloc[0]
|
4149
|
+
w_wma_8 = pktalib.WMA(weeklyData["close"],timeperiod=8).tail(1).iloc[0]
|
4150
|
+
w_sma_8 = pktalib.SMA(weeklyData["close"],timeperiod=8).tail(1).iloc[0]
|
4139
4151
|
numPreviousCandles = 20
|
4140
4152
|
pullbackData = data.head(numPreviousCandles)
|
4141
|
-
pullbackData.loc[:,'PullBack'] = pullbackData["
|
4153
|
+
pullbackData.loc[:,'PullBack'] = pullbackData["close"].lt(pullbackData["open"]) #.shift(periods=1)) #& data["low"].lt(data["low"].shift(periods=1))
|
4142
4154
|
shrinkedVolData = pullbackData[pullbackData["PullBack"] == True].head(numPreviousCandles)
|
4143
|
-
recentLargestVolume = max(pullbackData[pullbackData["PullBack"] == False].head(3)["
|
4144
|
-
# pullbackData.loc[:,'PBVolRatio'] = pullbackData["
|
4155
|
+
recentLargestVolume = max(pullbackData[pullbackData["PullBack"] == False].head(3)["volume"])
|
4156
|
+
# pullbackData.loc[:,'PBVolRatio'] = pullbackData["volume"]/recentLargestVolume
|
4145
4157
|
volInPreviousPullbacksShrinked = False
|
4146
4158
|
if not shrinkedVolData.empty:
|
4147
4159
|
index = 0
|
4148
4160
|
while index < len(shrinkedVolData):
|
4149
|
-
volInPreviousPullbacksShrinked = shrinkedVolData["
|
4161
|
+
volInPreviousPullbacksShrinked = shrinkedVolData["volume"].iloc[index] < self.configManager.vcpVolumeContractionRatio * recentLargestVolume
|
4150
4162
|
if not volInPreviousPullbacksShrinked:
|
4151
4163
|
break
|
4152
4164
|
index += 1
|
@@ -4187,18 +4199,18 @@ class ScreeningStatistics:
|
|
4187
4199
|
# Either the rolling volume of past 20 sessions or today's volume should be > min volume
|
4188
4200
|
hasMinimumVolume = (
|
4189
4201
|
recent["VolMA"].iloc[0] >= minVolume
|
4190
|
-
or recent["
|
4202
|
+
or recent["volume"].iloc[0] >= minVolume
|
4191
4203
|
)
|
4192
4204
|
if recent["VolMA"].iloc[0] == 0: # Handles Divide by 0 warning
|
4193
|
-
saveDict["
|
4194
|
-
screenDict["
|
4205
|
+
saveDict["volume"] = 0 # "Unknown"
|
4206
|
+
screenDict["volume"] = 0
|
4195
4207
|
return False, hasMinimumVolume
|
4196
|
-
ratio = round(recent["
|
4197
|
-
saveDict["
|
4208
|
+
ratio = round(recent["volume"].iloc[0] / recent["VolMA"].iloc[0], 2)
|
4209
|
+
saveDict["volume"] = ratio
|
4198
4210
|
if ratio >= volumeRatio and ratio != np.nan and (not math.isinf(ratio)):
|
4199
|
-
screenDict["
|
4211
|
+
screenDict["volume"] = ratio
|
4200
4212
|
return True, hasMinimumVolume
|
4201
|
-
screenDict["
|
4213
|
+
screenDict["volume"] = ratio
|
4202
4214
|
return False, hasMinimumVolume
|
4203
4215
|
|
4204
4216
|
# Find if stock is validating volume spread analysis
|
@@ -4213,23 +4225,23 @@ class ScreeningStatistics:
|
|
4213
4225
|
try:
|
4214
4226
|
# Check for previous RED candles
|
4215
4227
|
# Current candle = 0th, Previous Candle = 1st for following logic
|
4216
|
-
if data.iloc[1]["
|
4217
|
-
spread1 = abs(data.iloc[1]["
|
4218
|
-
spread0 = abs(data.iloc[0]["
|
4228
|
+
if data.iloc[1]["open"] >= data.iloc[1]["close"]:
|
4229
|
+
spread1 = abs(data.iloc[1]["open"] - data.iloc[1]["close"])
|
4230
|
+
spread0 = abs(data.iloc[0]["open"] - data.iloc[0]["close"])
|
4219
4231
|
lower_wick_spread0 = (
|
4220
|
-
max(data.iloc[0]["
|
4221
|
-
- data.iloc[0]["
|
4232
|
+
max(data.iloc[0]["open"], data.iloc[0]["close"])
|
4233
|
+
- data.iloc[0]["low"]
|
4222
4234
|
)
|
4223
|
-
vol1 = data.iloc[1]["
|
4224
|
-
vol0 = data.iloc[0]["
|
4235
|
+
vol1 = data.iloc[1]["volume"]
|
4236
|
+
vol0 = data.iloc[0]["volume"]
|
4225
4237
|
saved = self.findCurrentSavedValue(screenDict, saveDict, "Pattern")
|
4226
4238
|
if (
|
4227
4239
|
spread0 > spread1
|
4228
4240
|
and vol0 < vol1
|
4229
|
-
and data.iloc[0]["
|
4230
|
-
and data.iloc[0]["
|
4241
|
+
and data.iloc[0]["volume"] < data.iloc[0]["VolMA"]
|
4242
|
+
and data.iloc[0]["close"] <= data.iloc[1]["open"]
|
4231
4243
|
and spread0 < lower_wick_spread0
|
4232
|
-
and data.iloc[0]["
|
4244
|
+
and data.iloc[0]["volume"] <= int(data.iloc[1]["volume"] * 0.75)
|
4233
4245
|
):
|
4234
4246
|
screenDict["Pattern"] = (
|
4235
4247
|
saved[0]
|
@@ -4242,8 +4254,8 @@ class ScreeningStatistics:
|
|
4242
4254
|
if (
|
4243
4255
|
spread0 < spread1
|
4244
4256
|
and vol0 > vol1
|
4245
|
-
and data.iloc[0]["
|
4246
|
-
and data.iloc[0]["
|
4257
|
+
and data.iloc[0]["volume"] > data.iloc[0]["VolMA"]
|
4258
|
+
and data.iloc[0]["close"] <= data.iloc[1]["open"]
|
4247
4259
|
):
|
4248
4260
|
screenDict["Pattern"] = (
|
4249
4261
|
saved[0]
|