tranquilTrill commited on
Commit
3dbc640
ยท
verified ยท
1 Parent(s): df4db9b

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +299 -0
app.py ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import yfinance as yf
2
+ import pandas as pd
3
+ import numpy as np
4
+ import plotly.graph_objs as go
5
+ from sklearn.preprocessing import MinMaxScaler
6
+ from keras.src.models.sequential import Sequential
7
+ from keras.src.layers import LSTM, Dense
8
+ import streamlit as st
9
+ from keras.src.callbacks import EarlyStopping
10
+
11
+
12
+ # Fetch stock data
13
+ def fetch_data(ticker):
14
+ return yf.download(ticker, period='6mo')
15
+
16
+ # Add indicators
17
+ def add_indicators(df):
18
+ df['SMA_20'] = df['Close'].rolling(window=20).mean()
19
+ df['SMA_50'] = df['Close'].rolling(window=50).mean()
20
+
21
+ std_20 = df['Close'].rolling(window=20).std()
22
+ std_20 = std_20.squeeze() # ensure it's a Series
23
+
24
+ df['BB_Upper'] = df['SMA_20'] + 2 * std_20
25
+ df['BB_Lower'] = df['SMA_20'] - 2 * std_20
26
+
27
+ df['RSI'] = compute_rsi(df['Close'])
28
+ df['MACD'], df['MACD_Signal'], df['MACD_Hist'] = compute_macd(df['Close'])
29
+
30
+ return df
31
+
32
+ # RSI calculation
33
+ def compute_rsi(series, period=14):
34
+ delta = series.diff()
35
+ gain = delta.clip(lower=0)
36
+ loss = -delta.clip(upper=0)
37
+ avg_gain = gain.rolling(period).mean()
38
+ avg_loss = loss.rolling(period).mean()
39
+ rs = avg_gain / avg_loss
40
+ return 100 - (100 / (1 + rs))
41
+
42
+ # MACD calculation
43
+ def compute_macd(series):
44
+ ema12 = series.ewm(span=12, adjust=False).mean()
45
+ ema26 = series.ewm(span=26, adjust=False).mean()
46
+ macd = ema12 - ema26
47
+ signal = macd.ewm(span=9, adjust=False).mean()
48
+ hist = macd - signal
49
+ return macd, signal, hist
50
+
51
+ # --- Detect candlestick patterns manually ---
52
+ def detect_patterns(df, sma_period=5):
53
+ patterns = []
54
+ hover_texts = []
55
+
56
+ # Calculate Simple Moving Average for trend detection
57
+ df['SMA'] = df['Close'].rolling(window=sma_period).mean()
58
+ sma = df.Close.rolling(window=sma_period).mean()
59
+ # Helper functions for trend direction, with NaN handling
60
+ def is_downtrend(i):
61
+ sma = df.iloc[i].SMA
62
+ if sma is None:
63
+ return False
64
+ return (df['Close'].iloc[i] < df['SMA'].iloc[i]).bool()
65
+
66
+ def is_uptrend(i):
67
+ sma = df.iloc[i].SMA
68
+ if sma is None:
69
+ return False
70
+ return (df['Close'].iloc[i] > df['SMA'].iloc[i]).bool()
71
+
72
+ for i in range(2, len(df)):
73
+ # Skip if not enough data for trend analysis
74
+ if pd.isna(df['SMA'].iloc[i]):
75
+ patterns.append("None")
76
+ hover_texts.append("")
77
+ continue
78
+
79
+ candle = df.iloc[i]
80
+ o = candle.Open.values
81
+ h = candle.High.values
82
+ l = candle.Low.values
83
+ c = candle.Close.values
84
+
85
+ # Previous candle
86
+ prev_candle = df.iloc[i-1]
87
+ prev_o = prev_candle.Open.values
88
+ prev_h = prev_candle.High.values
89
+ prev_l = prev_candle.Low.values
90
+ prev_c = prev_candle.Close.values
91
+
92
+ # Two candles back
93
+ prev_candle2 = df.iloc[i-2]
94
+ prev2_o = prev_candle2.Open.values
95
+ prev2_c = prev_candle2.Close.values
96
+ prev2_h = prev_candle2.High.values
97
+ prev2_l = prev_candle2.Low.values
98
+
99
+ # Calculations
100
+ body = abs(c - o)
101
+ upper_shadow = h - max(o, c)
102
+ lower_shadow = min(o, c) - l
103
+ total_range = h - l if h != l else 0.0001
104
+ prev_body = abs(prev_c - prev_o)
105
+
106
+ pattern = ""
107
+
108
+ # === Pattern Detection ===
109
+ lowShadBiggerBody = lower_shadow >= 2 * body
110
+ upShadSmallerBody = upper_shadow <= body
111
+ closeBiggerOpen = c > o
112
+ openBiggerClose = c < o
113
+ downTrend = is_downtrend(i - 1)
114
+ upTrend = is_uptrend(i - 1)
115
+ upperShadBiggerBody = upper_shadow >= 2 * body
116
+ lowShadSmallerBody = lower_shadow <= body
117
+ prevCloseSmallerPrevOpen = prev_c < prev_o
118
+ openBiggerPrevClose = o >= prev_c
119
+ openSmallerPrevClose = o <= prev_c
120
+ closeBiggerPrevOpen = c >= prev_o
121
+ closeSmallerPrevOpen = c <= prev_o
122
+
123
+ if body <= 0.1 * total_range:
124
+ pattern = "Doji"
125
+
126
+ elif (lowShadBiggerBody & upShadSmallerBody & closeBiggerOpen & downTrend):
127
+ pattern = "Hammer"
128
+
129
+ elif (upperShadBiggerBody & lowShadSmallerBody & closeBiggerOpen & downTrend):
130
+ pattern = "Inverted Hammer"
131
+
132
+ elif (prevCloseSmallerPrevOpen & closeBiggerOpen & openSmallerPrevClose &
133
+ closeBiggerPrevOpen & (body > prev_body) & downTrend):
134
+ pattern = "Bullish Engulfing"
135
+
136
+ elif ((prev_c > prev_o) & openBiggerClose & openBiggerPrevClose &
137
+ closeSmallerPrevOpen & (body > prev_body) & upTrend):
138
+ pattern = "Bearish Engulfing"
139
+
140
+ elif ((upper_shadow >= 1.5 * body) & (lower_shadow <= 0.2 * body) &
141
+ openBiggerClose & upTrend):
142
+ pattern = "Shooting Star"
143
+
144
+ elif ((lower_shadow >= 2 * body) & (upper_shadow <= 0.2 * body) &
145
+ openBiggerClose & upTrend):
146
+ pattern = "Hanging Man"
147
+
148
+ elif (openBiggerClose & (body >= 0.6 * total_range) &
149
+ (upper_shadow <= 0.15 * total_range) & (lower_shadow <= 0.15 * total_range)):
150
+ pattern = "Dark Pool"
151
+
152
+ elif ((prev2_c < prev2_o) &
153
+ (abs(prev_c - prev_o) <= 0.3 * (prev_h - prev_l)) &
154
+ closeBiggerOpen & (c > (prev_o + prev_c) / 2) & downTrend):
155
+ pattern = "Morning Star"
156
+
157
+ elif ((prev2_c > prev2_o) &
158
+ (abs(prev_c - prev_o) <= 0.3 * (prev_h - prev_l)) &
159
+ openBiggerClose & (c < (prev_o + prev_c) / 2) & upTrend):
160
+ pattern = "Evening Star"
161
+
162
+ else:
163
+ pattern = "None"
164
+
165
+ hover_text = PATTERN_DESCRIPTIONS.get(pattern, "") if pattern != "None" else ""
166
+ patterns.append(pattern)
167
+ hover_texts.append(hover_text)
168
+
169
+ # Align output with sliced data
170
+ df = df.iloc[2:].copy()
171
+ df['Pattern'] = patterns
172
+ df['HoverText'] = hover_texts
173
+ df.drop(columns=['SMA'], inplace=True)
174
+ return df
175
+
176
+ PATTERN_DESCRIPTIONS = {
177
+ "Hammer": "Bullish reversal pattern after a downtrend.",
178
+ "Inverted Hammer": "Potential bullish reversal with a long upper wick.",
179
+ "Bullish Engulfing": "Strong bullish signal after a bearish candle.",
180
+ "Bearish Engulfing": "Strong bearish signal after a bullish candle.",
181
+ "Doji": "Market indecision; open and close are very close.",
182
+ "Shooting Star": "Bearish reversal after uptrend with long upper wick.",
183
+ "Hanging Man": "Bearish signal with long lower shadow after uptrend.",
184
+ "Dark Pool": "Heavy bearish candle with short shadows.",
185
+ "Morning Star": "Bullish 3-candle reversal pattern.",
186
+ "Evening Star": "Bearish 3-candle reversal pattern."
187
+ }
188
+
189
+ # Predict next close price using LSTM
190
+ def predict_next_price(df):
191
+ # Select features
192
+ features = ['Close', 'SMA_20', 'SMA_50', 'RSI', 'MACD', 'MACD_Signal']
193
+
194
+ # Drop NaNs caused by rolling indicators
195
+ df = df[features].dropna()
196
+
197
+ if len(df) < 25:
198
+ return None # not enough data
199
+
200
+ # Scale all features
201
+ scaler = MinMaxScaler()
202
+ scaled_data = scaler.fit_transform(df)
203
+
204
+ lookback = 20
205
+ X = []
206
+ y = []
207
+
208
+ for i in range(lookback, len(scaled_data)):
209
+ X.append(scaled_data[i-lookback:i]) # shape: (lookback, num_features)
210
+ y.append(scaled_data[i, 0]) # predict 'Close'
211
+
212
+ X = np.array(X)
213
+ y = np.array(y)
214
+
215
+ # ๐Ÿง  Build model that takes multiple features
216
+ model = Sequential()
217
+ model.add(LSTM(64, return_sequences=True, input_shape=(lookback, len(features))))
218
+ model.add(LSTM(32))
219
+ model.add(Dense(1))
220
+ model.compile(optimizer='adam', loss='mean_squared_error')
221
+ model.fit(X, y, epochs=20, batch_size=8, verbose=0)
222
+
223
+ # Predict next day
224
+ last_sequence = scaled_data[-lookback:]
225
+ last_sequence = np.reshape(last_sequence, (1, lookback, len(features)))
226
+ predicted_scaled = model.predict(last_sequence, verbose=0)
227
+
228
+ # ๐Ÿ”„ Inverse-transform: only the 'Close' feature
229
+ dummy = np.zeros((1, len(features)))
230
+ dummy[0, 0] = predicted_scaled[0, 0]
231
+ predicted_price = scaler.inverse_transform(dummy)[0, 0]
232
+
233
+ return predicted_price
234
+
235
+ # Chart generator
236
+ def plot_chart(df, ticker):
237
+ fig = go.Figure()
238
+ fig.add_trace(go.Scatter(x=df.index, y=df['Close'], name='Close', line=dict(color='blue')))
239
+ fig.add_trace(go.Scatter(x=df.index, y=df['SMA_20'], name='SMA 20', line=dict(color='orange')))
240
+ fig.add_trace(go.Scatter(x=df.index, y=df['SMA_50'], name='SMA 50', line=dict(color='purple')))
241
+ fig.add_trace(go.Scatter(x=df.index, y=df['BB_Upper'], name='BB Upper', line=dict(color='green', dash='dot')))
242
+ fig.add_trace(go.Scatter(x=df.index, y=df['BB_Lower'], name='BB Lower', line=dict(color='red', dash='dot')))
243
+ fig.update_layout(title=f"{ticker} Price with Indicators", xaxis_title="Date", yaxis_title="Price")
244
+ return fig
245
+
246
+ def plot_macd(df):
247
+ fig = go.Figure()
248
+ fig.add_trace(go.Scatter(x=df.index, y=df['MACD'], name='MACD', line=dict(color='blue')))
249
+ fig.add_trace(go.Scatter(x=df.index, y=df['MACD_Signal'], name='Signal', line=dict(color='orange')))
250
+ fig.add_trace(go.Bar(x=df.index, y=df['MACD_Hist'], name='Histogram', marker_color='gray'))
251
+ fig.update_layout(title='MACD line above the signal line -> (buy) signal โฌ†๏ธ, MACD line below the signal line -> (sell) signal โฌ‡๏ธ', xaxis_title='Date', yaxis_title='MACD')
252
+ return fig
253
+
254
+ # Streamlit dashboard
255
+ st.set_page_config("๐Ÿ“ˆ Smart Stock Analyzer", layout="wide")
256
+ st.title("๐Ÿ“Š Smart Stock Analyzer with AI & Indicators")
257
+
258
+ tickers_input = st.text_input("Enter tickers (comma-separated):", value="NVDA, MSFT, AAPL, META, GOOGL, QNTM, ITOCY, MUFG, MARUY, SHL.DE, BCS, ING")
259
+
260
+ tickers = [t.strip().upper() for t in tickers_input.split(",") if t.strip()]
261
+
262
+ for ticker in tickers:
263
+ st.header(f"๐Ÿ” Analyzing {ticker}")
264
+ try:
265
+ df = fetch_data(ticker)
266
+ df = df.copy()
267
+ patterns = detect_patterns(df, sma_period=5)
268
+ df = add_indicators(df)
269
+ current_price = df['Close'].iloc[-1].values[0]
270
+ predicted_price = predict_next_price(df)
271
+
272
+
273
+ col1, col2 = st.columns(2)
274
+
275
+ with col1:
276
+ st.write(patterns[patterns['Pattern'] != ''].tail(10))
277
+ #st.plotly_chart(plot_chart(df, ticker), use_container_width=True)
278
+ with col2:
279
+ st.plotly_chart(plot_macd(df), use_container_width=True)
280
+
281
+ st.subheader("๐Ÿ“ˆ Summary")
282
+ rsi = df['RSI'].iloc[-1]
283
+ st.markdown(f"- **RSI (above 70 = overbought conditions, below 30 = oversold conditions)**: {rsi:.2f} {'๐ŸŸข BUY' if rsi < 30 else '๐Ÿ”ด SELL' if rsi > 70 else '๐ŸŸก HOLD'}")
284
+ st.markdown(f"- **SMA Crossover (potentially signaling a shift in trend direction)**: {'โฌ†๏ธ Bullish' if df['SMA_20'].iloc[-1] > df['SMA_50'].iloc[-1] else 'โฌ‡๏ธ Bearish'}")
285
+
286
+ st.subheader("๐Ÿ”ฎ AI Price Prediction")
287
+ st.markdown(f"- **Current Price**: ${current_price:.2f}")
288
+ st.markdown(f"- **Predicted Next Close**: ${predicted_price:.2f}")
289
+ change = predicted_price - current_price
290
+ percent = (change / current_price) * 100
291
+ if percent > 3:
292
+ st.success("๐Ÿš€ AI says: Likely UP (>3%) โ€“ Consider BUYING")
293
+ elif percent < -3:
294
+ st.error("โš ๏ธ AI says: Likely DOWN (>3%) โ€“ Consider SELLING")
295
+ else:
296
+ st.info("๐Ÿ’ค AI says: Not much movement expected โ€“ HOLD")
297
+
298
+ except Exception as e:
299
+ st.error(f"Failed to analyze {ticker}: {e}")