Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1,3 +1,5 @@
|
|
|
|
|
|
1 |
import gradio as gr
|
2 |
import pandas as pd
|
3 |
import yfinance as yf
|
@@ -5,50 +7,47 @@ from datetime import datetime
|
|
5 |
import plotly.graph_objects as go
|
6 |
import numpy as np
|
7 |
|
8 |
-
#
|
|
|
9 |
def calculate_sma(df, window):
|
10 |
-
"""Calculate Simple Moving Average (SMA)."""
|
11 |
return df['Close'].rolling(window=window).mean()
|
12 |
|
13 |
def calculate_ema(df, window):
|
14 |
-
"""Calculate Exponential Moving Average (EMA)."""
|
15 |
return df['Close'].ewm(span=window, adjust=False).mean()
|
16 |
|
|
|
17 |
def calculate_macd(df):
|
18 |
-
"""Calculate MACD and its Signal Line."""
|
19 |
short_ema = df['Close'].ewm(span=12, adjust=False).mean()
|
20 |
long_ema = df['Close'].ewm(span=26, adjust=False).mean()
|
21 |
macd = short_ema - long_ema
|
22 |
signal = macd.ewm(span=9, adjust=False).mean()
|
23 |
return macd, signal
|
24 |
|
25 |
-
|
26 |
-
|
27 |
delta = df['Close'].diff()
|
28 |
-
gain = delta.where(delta > 0, 0).rolling(window=
|
29 |
-
loss = -delta.where(delta < 0, 0).rolling(window=
|
30 |
rs = gain / loss
|
31 |
rsi = 100 - (100 / (1 + rs))
|
32 |
return rsi
|
33 |
|
34 |
-
def calculate_bollinger_bands(df
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
upper_bb = middle_bb + (2 * std_dev)
|
39 |
-
lower_bb = middle_bb - (2 * std_dev)
|
40 |
return middle_bb, upper_bb, lower_bb
|
41 |
|
42 |
-
def calculate_stochastic_oscillator(df
|
43 |
-
|
44 |
-
|
45 |
-
highest_high = df['High'].rolling(window=window).max()
|
46 |
slowk = ((df['Close'] - lowest_low) / (highest_high - lowest_low)) * 100
|
47 |
slowd = slowk.rolling(window=3).mean()
|
48 |
return slowk, slowd
|
49 |
|
|
|
|
|
50 |
def calculate_cmf(df, window=20):
|
51 |
-
"""Calculate Chaikin Money Flow (CMF)."""
|
52 |
mfv = ((df['Close'] - df['Low']) - (df['High'] - df['Close'])) / (df['High'] - df['Low']) * df['Volume']
|
53 |
cmf = mfv.rolling(window=window).sum() / df['Volume'].rolling(window=window).sum()
|
54 |
return cmf
|
@@ -61,8 +60,10 @@ def calculate_cci(df, window=20):
|
|
61 |
cci = (typical_price - sma) / (0.015 * mean_deviation)
|
62 |
return cci
|
63 |
|
|
|
|
|
64 |
def generate_trading_signals(df):
|
65 |
-
|
66 |
df['SMA_30'] = calculate_sma(df, 30)
|
67 |
df['SMA_100'] = calculate_sma(df, 100)
|
68 |
df['EMA_12'] = calculate_ema(df, 12)
|
@@ -73,102 +74,224 @@ def generate_trading_signals(df):
|
|
73 |
df['CMF'] = calculate_cmf(df)
|
74 |
df['CCI'] = calculate_cci(df)
|
75 |
|
76 |
-
|
|
|
|
|
|
|
|
|
77 |
macd, signal = calculate_macd(df)
|
78 |
df['MACD_Signal'] = np.select([(macd > signal) & (macd.shift(1) <= signal.shift(1)),
|
79 |
-
|
80 |
-
[1, -1], default=0)
|
81 |
-
|
82 |
-
# Stricter thresholds for signals
|
83 |
-
df['RSI_Signal'] = np.where(df['RSI'] < 5, 1, np.where(df['RSI'] > 95, -1, 0))
|
84 |
-
df['BB_Signal'] = np.where(df['Close'] < df['LowerBB'] - 2 * df['Close'].rolling(window=20).std(), 1,
|
85 |
-
np.where(df['Close'] > df['UpperBB'] + 2 * df['Close'].rolling(window=20).std(), -1, 0))
|
86 |
-
df['Stochastic_Signal'] = np.where((df['SlowK'] < 5) & (df['SlowD'] < 5), 1,
|
87 |
-
np.where((df['SlowK'] > 95) & (df['SlowD'] > 95), -1, 0))
|
88 |
-
df['CMF_Signal'] = np.where(df['CMF'] > 0.4, -1, np.where(df['CMF'] < -0.4, 1, 0))
|
89 |
-
df['CCI_Signal'] = np.where(df['CCI'] < -200, 1, np.where(df['CCI'] > 200, -1, 0))
|
90 |
|
91 |
-
# Combined signal for stronger buy/sell points
|
92 |
-
df['Combined_Signal'] = df[['RSI_Signal', 'BB_Signal',
|
93 |
-
'Stochastic_Signal', 'CMF_Signal',
|
94 |
-
'CCI_Signal']].sum(axis=1)
|
95 |
|
96 |
-
|
97 |
-
df['
|
98 |
-
df['
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
|
100 |
return df
|
101 |
|
102 |
|
103 |
-
#
|
104 |
def plot_combined_signals(df, ticker):
|
105 |
-
|
106 |
fig = go.Figure()
|
107 |
-
fig.add_trace(go.Scatter(x=df.index, y=df['Close'], mode='lines', name='Closing Price',
|
108 |
-
line=dict(color='lightcoral', width=2)))
|
109 |
|
110 |
-
|
111 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
112 |
|
113 |
-
|
114 |
-
|
115 |
-
fig.add_trace(go.Scatter(
|
116 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
117 |
|
118 |
-
fig.update_layout(title=f'{ticker}: Combined Trading Signals',
|
119 |
-
xaxis=dict(title='Date'), yaxis=dict(title='Price'),
|
120 |
-
plot_bgcolor='black', paper_bgcolor='black', font=dict(color='white'))
|
121 |
return fig
|
122 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
def plot_individual_signals(df, ticker):
|
124 |
-
|
125 |
fig = go.Figure()
|
126 |
-
fig.add_trace(go.Scatter(
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
return fig
|
142 |
|
143 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
144 |
def stock_analysis(ticker, start_date, end_date):
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
150 |
|
151 |
-
combined_fig = plot_combined_signals(df_last_90, ticker)
|
152 |
-
individual_fig = plot_individual_signals(df_last_90, ticker)
|
153 |
|
154 |
-
return combined_fig, individual_fig
|
155 |
-
except Exception as e:
|
156 |
-
return f"Error: {str(e)}", None
|
157 |
|
158 |
-
#
|
159 |
-
|
|
|
160 |
gr.Markdown("## Stock Market Analysis App")
|
161 |
|
162 |
-
ticker_input = gr.Textbox(label="Stock Ticker", value="
|
163 |
start_date_input = gr.Textbox(label="Start Date (YYYY-MM-DD)", value="2022-01-01")
|
164 |
end_date_input = gr.Textbox(label="End Date (YYYY-MM-DD)", value="2026-01-01")
|
165 |
|
166 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
167 |
|
168 |
-
|
169 |
-
|
|
|
170 |
|
171 |
-
|
172 |
-
|
173 |
|
174 |
-
app.launch()
|
|
|
1 |
+
# %%
|
2 |
+
# %%
|
3 |
import gradio as gr
|
4 |
import pandas as pd
|
5 |
import yfinance as yf
|
|
|
7 |
import plotly.graph_objects as go
|
8 |
import numpy as np
|
9 |
|
10 |
+
# Functions for calculating indicators (SMA, RSI, etc.) and generating trading signals
|
11 |
+
|
12 |
def calculate_sma(df, window):
|
|
|
13 |
return df['Close'].rolling(window=window).mean()
|
14 |
|
15 |
def calculate_ema(df, window):
|
|
|
16 |
return df['Close'].ewm(span=window, adjust=False).mean()
|
17 |
|
18 |
+
|
19 |
def calculate_macd(df):
|
|
|
20 |
short_ema = df['Close'].ewm(span=12, adjust=False).mean()
|
21 |
long_ema = df['Close'].ewm(span=26, adjust=False).mean()
|
22 |
macd = short_ema - long_ema
|
23 |
signal = macd.ewm(span=9, adjust=False).mean()
|
24 |
return macd, signal
|
25 |
|
26 |
+
|
27 |
+
def calculate_rsi(df):
|
28 |
delta = df['Close'].diff()
|
29 |
+
gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
|
30 |
+
loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
|
31 |
rs = gain / loss
|
32 |
rsi = 100 - (100 / (1 + rs))
|
33 |
return rsi
|
34 |
|
35 |
+
def calculate_bollinger_bands(df):
|
36 |
+
middle_bb = df['Close'].rolling(window=20).mean()
|
37 |
+
upper_bb = middle_bb + 2 * df['Close'].rolling(window=20).std()
|
38 |
+
lower_bb = middle_bb - 2 * df['Close'].rolling(window=20).std()
|
|
|
|
|
39 |
return middle_bb, upper_bb, lower_bb
|
40 |
|
41 |
+
def calculate_stochastic_oscillator(df):
|
42 |
+
lowest_low = df['Low'].rolling(window=14).min()
|
43 |
+
highest_high = df['High'].rolling(window=14).max()
|
|
|
44 |
slowk = ((df['Close'] - lowest_low) / (highest_high - lowest_low)) * 100
|
45 |
slowd = slowk.rolling(window=3).mean()
|
46 |
return slowk, slowd
|
47 |
|
48 |
+
|
49 |
+
|
50 |
def calculate_cmf(df, window=20):
|
|
|
51 |
mfv = ((df['Close'] - df['Low']) - (df['High'] - df['Close'])) / (df['High'] - df['Low']) * df['Volume']
|
52 |
cmf = mfv.rolling(window=window).sum() / df['Volume'].rolling(window=window).sum()
|
53 |
return cmf
|
|
|
60 |
cci = (typical_price - sma) / (0.015 * mean_deviation)
|
61 |
return cci
|
62 |
|
63 |
+
|
64 |
+
|
65 |
def generate_trading_signals(df):
|
66 |
+
# Calculate various indicators
|
67 |
df['SMA_30'] = calculate_sma(df, 30)
|
68 |
df['SMA_100'] = calculate_sma(df, 100)
|
69 |
df['EMA_12'] = calculate_ema(df, 12)
|
|
|
74 |
df['CMF'] = calculate_cmf(df)
|
75 |
df['CCI'] = calculate_cci(df)
|
76 |
|
77 |
+
|
78 |
+
|
79 |
+
# Generate trading signals
|
80 |
+
df['SMA_Signal'] = np.where(df['SMA_30'] > df['SMA_100'], 1, 0)
|
81 |
+
|
82 |
macd, signal = calculate_macd(df)
|
83 |
df['MACD_Signal'] = np.select([(macd > signal) & (macd.shift(1) <= signal.shift(1)),
|
84 |
+
(macd < signal) & (macd.shift(1) >= signal.shift(1))],[1, -1], default=0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
|
|
|
|
|
|
|
|
|
86 |
|
87 |
+
|
88 |
+
df['RSI_Signal'] = np.where(df['RSI'] < 20, 1, 0)
|
89 |
+
df['RSI_Signal'] = np.where(df['RSI'] > 90, -1, df['RSI_Signal'])
|
90 |
+
|
91 |
+
df['BB_Signal'] = np.where(df['Close'] < df['LowerBB'], 0, 0)
|
92 |
+
df['BB_Signal'] = np.where(df['Close'] > df['UpperBB'], -1, df['BB_Signal'])
|
93 |
+
|
94 |
+
df['Stochastic_Signal'] = np.where((df['SlowK'] < 10) & (df['SlowD'] < 15), 1, 0)
|
95 |
+
df['Stochastic_Signal'] = np.where((df['SlowK'] > 90) & (df['SlowD'] > 85), -1, df['Stochastic_Signal'])
|
96 |
+
|
97 |
+
|
98 |
+
df['CMF_Signal'] = np.where(df['CMF'] > 0.3, -1, np.where(df['CMF'] < -0.3, 1, 0))
|
99 |
+
|
100 |
+
|
101 |
+
df['CCI_Signal'] = np.where(df['CCI'] < -180, 1, 0)
|
102 |
+
df['CCI_Signal'] = np.where(df['CCI'] > 150, -1, df['CCI_Signal'])
|
103 |
+
|
104 |
+
|
105 |
+
|
106 |
+
# Combined signal for stronger buy/sell points
|
107 |
+
df['Combined_Signal'] = df[['RSI_Signal', 'BB_Signal',
|
108 |
+
'Stochastic_Signal', 'CMF_Signal',
|
109 |
+
'CCI_Signal']].sum(axis=1)
|
110 |
|
111 |
return df
|
112 |
|
113 |
|
114 |
+
# %%
|
115 |
def plot_combined_signals(df, ticker):
|
116 |
+
# Create a figure
|
117 |
fig = go.Figure()
|
|
|
|
|
118 |
|
119 |
+
# Add closing price trace
|
120 |
+
fig.add_trace(go.Scatter(
|
121 |
+
x=df.index, y=df['Close'],
|
122 |
+
mode='lines',
|
123 |
+
name='Closing Price',
|
124 |
+
line=dict(color='lightcoral', width=2)
|
125 |
+
))
|
126 |
+
|
127 |
+
# Add buy signals
|
128 |
+
buy_signals = df[df['Combined_Signal'] >= 3]
|
129 |
+
fig.add_trace(go.Scatter(
|
130 |
+
x=buy_signals.index, y=buy_signals['Close'],
|
131 |
+
mode='markers',
|
132 |
+
marker=dict(symbol='triangle-up', size=10, color='lightgreen'),
|
133 |
+
name='Buy Signal'
|
134 |
+
))
|
135 |
|
136 |
+
# Add sell signals
|
137 |
+
sell_signals = df[df['Combined_Signal'] <= -3]
|
138 |
+
fig.add_trace(go.Scatter(
|
139 |
+
x=sell_signals.index, y=sell_signals['Close'],
|
140 |
+
mode='markers',
|
141 |
+
marker=dict(symbol='triangle-down', size=10, color='lightsalmon'),
|
142 |
+
name='Sell Signal'
|
143 |
+
))
|
144 |
+
|
145 |
+
# Combined signal trace
|
146 |
+
fig.add_trace(go.Scatter(
|
147 |
+
x=df.index, y=df['Combined_Signal'],
|
148 |
+
mode='lines',
|
149 |
+
name='Combined Signal',
|
150 |
+
line=dict(color='deepskyblue', width=2),
|
151 |
+
yaxis='y2'
|
152 |
+
))
|
153 |
+
|
154 |
+
# Update layout
|
155 |
+
fig.update_layout(
|
156 |
+
title=f'{ticker}: 360 Stock Price and Combined Trading Signal',
|
157 |
+
xaxis=dict(title='Date'),
|
158 |
+
yaxis=dict(title='Price', side='left'),
|
159 |
+
yaxis2=dict(title='Combined Signal', overlaying='y', side='right', showgrid=False),
|
160 |
+
plot_bgcolor='black',
|
161 |
+
paper_bgcolor='black',
|
162 |
+
font=dict(color='white')
|
163 |
+
)
|
164 |
|
|
|
|
|
|
|
165 |
return fig
|
166 |
|
167 |
+
# %%
|
168 |
+
def stock_analysis(ticker, start_date, end_date):
|
169 |
+
# Download stock data from Yahoo Finance
|
170 |
+
df = yf.download(ticker, start=start_date, end=end_date)
|
171 |
+
|
172 |
+
# Generate signals
|
173 |
+
generate_trading_signals(df)
|
174 |
+
|
175 |
+
# Last 60 days
|
176 |
+
df_last_60 = df.tail(120)
|
177 |
+
|
178 |
+
# Plot signals
|
179 |
+
fig_signals = plot_combined_signals(df_last_60, ticker)
|
180 |
+
|
181 |
+
return fig_signals
|
182 |
+
|
183 |
+
|
184 |
+
|
185 |
+
|
186 |
+
# %%
|
187 |
def plot_individual_signals(df, ticker):
|
188 |
+
# Create a figure
|
189 |
fig = go.Figure()
|
190 |
+
fig.add_trace(go.Scatter(
|
191 |
+
x=df.index, y=df['Close'],
|
192 |
+
mode='lines',
|
193 |
+
name='Closing Price',
|
194 |
+
line=dict(color='lightcoral', width=2)
|
195 |
+
))
|
196 |
+
|
197 |
+
# Add buy/sell signals for each indicator
|
198 |
+
signal_names = ['RSI_Signal', 'BB_Signal',
|
199 |
+
'Stochastic_Signal', 'CMF_Signal',
|
200 |
+
'CCI_Signal']
|
201 |
+
|
202 |
+
for signal in signal_names:
|
203 |
+
buy_signals = df[df[signal] == 1]
|
204 |
+
sell_signals = df[df[signal] == -1]
|
205 |
+
|
206 |
+
fig.add_trace(go.Scatter(
|
207 |
+
x=buy_signals.index, y=buy_signals['Close'],
|
208 |
+
mode='markers',
|
209 |
+
marker=dict(symbol='triangle-up', size=10, color='lightgreen'),
|
210 |
+
name=f'{signal} Buy Signal'
|
211 |
+
))
|
212 |
+
|
213 |
+
fig.add_trace(go.Scatter(
|
214 |
+
x=sell_signals.index, y=sell_signals['Close'],
|
215 |
+
mode='markers',
|
216 |
+
marker=dict(symbol='triangle-down', size=10, color='lightsalmon'),
|
217 |
+
name=f'{signal} Sell Signal'
|
218 |
+
))
|
219 |
+
|
220 |
+
fig.update_layout(
|
221 |
+
title=f'{ticker}: Individual Trading Signals',
|
222 |
+
xaxis=dict(title='Date'),
|
223 |
+
yaxis=dict(title='Price', side='left'),
|
224 |
+
plot_bgcolor='black',
|
225 |
+
paper_bgcolor='black',
|
226 |
+
font=dict(color='white')
|
227 |
+
)
|
228 |
+
|
229 |
return fig
|
230 |
|
231 |
+
|
232 |
+
def display_signals(df):
|
233 |
+
# Create a signals DataFrame
|
234 |
+
signals_df = df[['Close', 'SMA_Signal', 'MACD_Signal', 'RSI_Signal',
|
235 |
+
'BB_Signal', 'Stochastic_Signal',
|
236 |
+
'CMF_Signal', 'CCI_Signal']].copy()
|
237 |
+
|
238 |
+
# The Date is the index, so we don't need to add it as a column
|
239 |
+
signals_df.index.name = 'Date' # Name the index for better readability
|
240 |
+
|
241 |
+
# Replace signal values with 'Buy', 'Sell', or 'Hold'
|
242 |
+
for column in signals_df.columns:
|
243 |
+
signals_df[column] = signals_df[column].replace(
|
244 |
+
{1: 'Buy', -1: 'Sell', 0: 'Hold'}
|
245 |
+
)
|
246 |
+
|
247 |
+
return signals_df
|
248 |
+
|
249 |
def stock_analysis(ticker, start_date, end_date):
|
250 |
+
# Download stock data from Yahoo Finance
|
251 |
+
df = yf.download(ticker, start=start_date, end=end_date)
|
252 |
+
|
253 |
+
# Generate signals
|
254 |
+
df = generate_trading_signals(df)
|
255 |
+
|
256 |
+
# Last 60 days for plotting
|
257 |
+
df_last_60 = df.tail(360)
|
258 |
+
|
259 |
+
# Plot combined signals
|
260 |
+
fig_signals = plot_combined_signals(df_last_60, ticker)
|
261 |
+
|
262 |
+
# Plot individual signals
|
263 |
+
fig_individual_signals = plot_individual_signals(df_last_60, ticker)
|
264 |
+
|
265 |
+
# Display signals DataFrame
|
266 |
+
signals_df = df_last_60[['Close', 'SMA_Signal', 'MACD_Signal', 'RSI_Signal', 'BB_Signal',
|
267 |
+
'Stochastic_Signal','CMF_Signal',
|
268 |
+
'CCI_Signal']]
|
269 |
+
|
270 |
+
return fig_signals, fig_individual_signals
|
271 |
|
|
|
|
|
272 |
|
|
|
|
|
|
|
273 |
|
274 |
+
# %%
|
275 |
+
# Define Gradio interface
|
276 |
+
with gr.Blocks() as demo:
|
277 |
gr.Markdown("## Stock Market Analysis App")
|
278 |
|
279 |
+
ticker_input = gr.Textbox(label="Enter Stock Ticker (e.g., AAPL, NVDA)", value="NVDA")
|
280 |
start_date_input = gr.Textbox(label="Start Date (YYYY-MM-DD)", value="2022-01-01")
|
281 |
end_date_input = gr.Textbox(label="End Date (YYYY-MM-DD)", value="2026-01-01")
|
282 |
|
283 |
+
# Create a submit button that runs the stock analysis function
|
284 |
+
button = gr.Button("Analyze Stock")
|
285 |
+
|
286 |
+
# Outputs: Display results, charts
|
287 |
+
combined_signals_output = gr.Plot(label="Combined Trading Signals")
|
288 |
+
individual_signals_output = gr.Plot(label="Individual Trading Signals")
|
289 |
+
#signals_df_output = gr.Dataframe(label="Buy/Sell Signals")
|
290 |
|
291 |
+
# Link button to function
|
292 |
+
button.click(stock_analysis, inputs=[ticker_input, start_date_input, end_date_input],
|
293 |
+
outputs=[combined_signals_output, individual_signals_output])
|
294 |
|
295 |
+
# Launch the interface
|
296 |
+
demo.launch()
|
297 |
|
|