chrisvnz commited on
Commit
cee6a7f
·
1 Parent(s): 57eee16

Update app.py

Browse files

Updated swing candle detection methods, much more reliable now

Files changed (1) hide show
  1. app.py +127 -14
app.py CHANGED
@@ -7,6 +7,116 @@ import pandas_ta as ta
7
  from scipy.signal import find_peaks
8
  import csv
9
  import os
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  def process_csv(csv_file, lookback, bullish_stoch_value, bearish_stoch_value, check_bullish_swing, check_bearish_swing):
12
 
@@ -24,21 +134,25 @@ def process_csv(csv_file, lookback, bullish_stoch_value, bearish_stoch_value, ch
24
  bearish_tickers = []
25
  for ticker in all_tickers:
26
  print(f"Processing {ticker} ({all_tickers.index(ticker)+1}/{ttl_tickers})")
 
27
  try:
28
  data = yf.Ticker(ticker)
29
  hist = data.history(period="1y", actions=False)
30
  if not hist.empty:
 
31
  hist.ta.ema(close='Close', length=20, append=True)
32
  hist.ta.ema(close='Close', length=50, append=True)
33
  hist.ta.ema(close='Close', length=100, append=True)
34
  hist.ta.sma(close='Close', length=150, append=True)
35
  stoch = hist.ta.stoch(high='High', low='Low', close='Close')
36
- peaks, _ = find_peaks(hist['Close'].values)
37
- valleys, _ = find_peaks(-hist['Close'].values)
38
- if all(hist['EMA_20'][-lookback:] > hist['EMA_50'][-lookback:]) and all(hist['EMA_50'][-lookback:] > hist['EMA_100'][-lookback:]) and all(hist['EMA_100'][-lookback:] > hist['SMA_150'][-lookback:]) and stoch['STOCHk_14_3_3'][-1] < bullish_stoch_value and (not check_bullish_swing or (peaks[-1] > peaks[-2] and valleys[-1] > valleys[-2])):
39
- bullish_tickers.append([ticker, hist['Close'][-1], hist['Volume'][-1], 'Bullish'])
40
- elif all(hist['EMA_20'][-lookback:] < hist['EMA_50'][-lookback:]) and all(hist['EMA_50'][-lookback:] < hist['EMA_100'][-lookback:]) and all(hist['EMA_100'][-lookback:] < hist['SMA_150'][-lookback:]) and stoch['STOCHk_14_3_3'][-1] > bearish_stoch_value and (not check_bearish_swing or (peaks[-1] < peaks[-2] and valleys[-1] < valleys[-2])):
41
- bearish_tickers.append([ticker, hist['Close'][-1], hist['Volume'][-1], 'Bearish'])
 
 
42
 
43
  except Exception as e:
44
  print(f"An error occurred with ticker {ticker}: {e}")
@@ -65,13 +179,13 @@ def process_csv(csv_file, lookback, bullish_stoch_value, bearish_stoch_value, ch
65
 
66
  iface = gr.Interface(
67
  fn=process_csv,
68
- inputs=[
69
  InputFile(label="Upload CSV"),
70
- InputNumber(label="EMA Loockback period (days, min 5 and max 60)", value=10),
71
- InputNumber(label="Bullish Stochastic Value (below)", value=30),
72
- InputNumber(label="Bearish Stochastic Value (above)", value=70),
73
- InputCheckbox(label="Check Bullish Swing (HH + HL) (beta)"),
74
- InputCheckbox(label="Check Bearish Swing (LH + LL) (beta)")
75
  ],
76
  outputs=[
77
  OutputFile(label="Download Bullish XLSX"),
@@ -82,8 +196,7 @@ iface = gr.Interface(
82
  title="Stock Analysis",
83
  description="""Upload a CSV file with a column named 'Ticker' (from TradingView or other) and
84
  get a filtered shortlist of bullish and bearish stocks in return.
85
- The tool will find 'stacked' EMAs + SMAs and check for Stochastics above or below the set values.
86
- Optionally choose to filter stocks with consistent trends in recent swing highs and lows"""
87
  )
88
 
89
  iface.launch()
 
7
  from scipy.signal import find_peaks
8
  import csv
9
  import os
10
+ import peakutils
11
+ from scipy.signal import argrelextrema
12
+ from collections import deque
13
+ import numpy as np
14
+
15
+ def getHigherLows(data: np.array, order=5, K=2):
16
+ # Get lows
17
+ low_idx = argrelextrema(data, np.less, order=order)[0]
18
+ lows = data[low_idx]
19
+ # Ensure consecutive lows are higher than previous lows
20
+ extrema = []
21
+ ex_deque = deque(maxlen=K)
22
+ for i, idx in enumerate(low_idx):
23
+ if i == 0:
24
+ ex_deque.append(idx)
25
+ continue
26
+ if lows[i] < lows[i-1]:
27
+ ex_deque.clear()
28
+
29
+ ex_deque.append(idx)
30
+ if len(ex_deque) == K:
31
+ extrema.append(ex_deque.copy())
32
+
33
+ return extrema
34
+
35
+ def getLowerHighs(data: np.array, order=5, K=2):
36
+ # Get highs
37
+ high_idx = argrelextrema(data, np.greater, order=order)[0]
38
+ highs = data[high_idx]
39
+ # Ensure consecutive highs are lower than previous highs
40
+ extrema = []
41
+ ex_deque = deque(maxlen=K)
42
+ for i, idx in enumerate(high_idx):
43
+ if i == 0:
44
+ ex_deque.append(idx)
45
+ continue
46
+ if highs[i] > highs[i-1]:
47
+ ex_deque.clear()
48
+
49
+ ex_deque.append(idx)
50
+ if len(ex_deque) == K:
51
+ extrema.append(ex_deque.copy())
52
+
53
+ return extrema
54
+
55
+ def getHigherHighs(data: np.array, order=5, K=2):
56
+ # Get highs
57
+ high_idx = argrelextrema(data, np.greater, order=5)[0]
58
+ highs = data[high_idx]
59
+ # Ensure consecutive highs are higher than previous highs
60
+ extrema = []
61
+ ex_deque = deque(maxlen=K)
62
+ for i, idx in enumerate(high_idx):
63
+ if i == 0:
64
+ ex_deque.append(idx)
65
+ continue
66
+ if highs[i] < highs[i-1]:
67
+ ex_deque.clear()
68
+
69
+ ex_deque.append(idx)
70
+ if len(ex_deque) == K:
71
+ extrema.append(ex_deque.copy())
72
+
73
+ return extrema
74
+
75
+ def getLowerLows(data: np.array, order=5, K=2):
76
+ # Get lows
77
+ low_idx = argrelextrema(data, np.less, order=order)[0]
78
+ lows = data[low_idx]
79
+ # Ensure consecutive lows are lower than previous lows
80
+ extrema = []
81
+ ex_deque = deque(maxlen=K)
82
+ for i, idx in enumerate(low_idx):
83
+ if i == 0:
84
+ ex_deque.append(idx)
85
+ continue
86
+ if lows[i] > lows[i-1]:
87
+ ex_deque.clear()
88
+
89
+ ex_deque.append(idx)
90
+ if len(ex_deque) == K:
91
+ extrema.append(ex_deque.copy())
92
+
93
+ return extrema
94
+
95
+
96
+ def check_trend(hist):
97
+
98
+ close = hist['Close'].values
99
+ order = 5
100
+ K = 2
101
+
102
+ hh = getHigherHighs(close, order, K)
103
+ hl = getHigherLows(close, order, K)
104
+ ll = getLowerLows(close, order, K)
105
+ lh = getLowerHighs(close, order, K)
106
+
107
+ # Get the most recent top and bottom labels
108
+ top_labels = hh if len(hh) > 0 and hh[-1][-1] > (lh[-1][-1] if len(lh) > 0 else 0) else lh
109
+ bottom_labels = hl if len(hl) > 0 and hl[-1][-1] > (ll[-1][-1] if len(ll) > 0 else 0) else ll
110
+
111
+ # Check if the most recent top and bottom labels form a pair
112
+ if top_labels == hh and bottom_labels == hl:
113
+ return True, 'bullish'
114
+ elif top_labels == lh and bottom_labels == ll:
115
+ return True, 'bearish'
116
+ else:
117
+ return False, 'uncertain'
118
+
119
+
120
 
121
  def process_csv(csv_file, lookback, bullish_stoch_value, bearish_stoch_value, check_bullish_swing, check_bearish_swing):
122
 
 
134
  bearish_tickers = []
135
  for ticker in all_tickers:
136
  print(f"Processing {ticker} ({all_tickers.index(ticker)+1}/{ttl_tickers})")
137
+
138
  try:
139
  data = yf.Ticker(ticker)
140
  hist = data.history(period="1y", actions=False)
141
  if not hist.empty:
142
+
143
  hist.ta.ema(close='Close', length=20, append=True)
144
  hist.ta.ema(close='Close', length=50, append=True)
145
  hist.ta.ema(close='Close', length=100, append=True)
146
  hist.ta.sma(close='Close', length=150, append=True)
147
  stoch = hist.ta.stoch(high='High', low='Low', close='Close')
148
+
149
+ trend_exists, trend_type = check_trend(hist)
150
+
151
+ if all(hist['EMA_20'][-lookback:] > hist['EMA_50'][-lookback:]) and all(hist['EMA_50'][-lookback:] > hist['EMA_100'][-lookback:]) and all(hist['EMA_100'][-lookback:] > hist['SMA_150'][-lookback:]) and stoch['STOCHk_14_3_3'][-1] <= bullish_stoch_value and trend_exists and trend_type == 'bullish':
152
+ bullish_tickers.append([ticker, hist['Close'][-1], hist['Volume'][-1], 'Bullish'])
153
+ elif all(hist['EMA_20'][-lookback:] < hist['EMA_50'][-lookback:]) and all(hist['EMA_50'][-lookback:] < hist['EMA_100'][-lookback:]) and all(hist['EMA_100'][-lookback:] < hist['SMA_150'][-lookback:]) and stoch['STOCHk_14_3_3'][-1] >= bearish_stoch_value and trend_exists and trend_type == 'bearish':
154
+ bearish_tickers.append([ticker, hist['Close'][-1], hist['Volume'][-1], 'Bearish'])
155
+
156
 
157
  except Exception as e:
158
  print(f"An error occurred with ticker {ticker}: {e}")
 
179
 
180
  iface = gr.Interface(
181
  fn=process_csv,
182
+ inputs=[
183
  InputFile(label="Upload CSV"),
184
+ InputNumber(label="EMA Loockback period (days, min 5 and max 60)", value=20),
185
+ InputNumber(label="Bullish Stochastic Value (equal or below)", value=30),
186
+ InputNumber(label="Bearish Stochastic Value (equal or above)", value=70),
187
+ InputCheckbox(label="Check Bullish Swing (HH + HL) (slower, in beta)"),
188
+ InputCheckbox(label="Check Bearish Swing (LH + LL) (slower, in beta)")
189
  ],
190
  outputs=[
191
  OutputFile(label="Download Bullish XLSX"),
 
196
  title="Stock Analysis",
197
  description="""Upload a CSV file with a column named 'Ticker' (from TradingView or other) and
198
  get a filtered shortlist of bullish and bearish stocks in return.
199
+ The tool will find 'stacked' EMAs + SMAs and check for Stochastics above or below the set values."""
 
200
  )
201
 
202
  iface.launch()