JayLacoma commited on
Commit
bf70086
·
verified ·
1 Parent(s): 14a8d47

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +267 -174
app.py CHANGED
@@ -1,21 +1,16 @@
1
- # %%
2
- # %%
3
  import gradio as gr
4
  import pandas as pd
5
  import yfinance as yf
6
- from datetime import datetime
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()
@@ -23,7 +18,6 @@ def calculate_macd(df):
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()
@@ -45,23 +39,18 @@ def calculate_stochastic_oscillator(df):
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
54
 
55
  def calculate_cci(df, window=20):
56
- """Calculate Commodity Channel Index (CCI)."""
57
  typical_price = (df['High'] + df['Low'] + df['Close']) / 3
58
  sma = typical_price.rolling(window=window).mean()
59
  mean_deviation = (typical_price - sma).abs().rolling(window=window).mean()
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)
@@ -74,228 +63,332 @@ def generate_trading_signals(df):
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
- # Modified RSI Signals (tighter thresholds)
88
- df['RSI_Signal'] = np.where(df['RSI'] < 12, 1, 0) # Changed from 20 to 15
89
- df['RSI_Signal'] = np.where(df['RSI'] > 95, -1, df['RSI_Signal']) # Changed from 90 to 95
90
 
 
 
 
91
 
92
- # Modified Bollinger Bands (require confirmation)
93
-
94
  buffer_percentage = 0.01 # 1% buffer
95
-
96
- # Buy signal: Price below LowerBB for 2 consecutive periods with buffer
97
  df['BB_Signal'] = np.where(
98
  (df['Close'] < df['LowerBB'] * (1 - buffer_percentage)) &
99
  (df['Close'].shift(1) < df['LowerBB'].shift(1) * (1 - buffer_percentage)) &
100
  (df['Close'].shift(2) < df['LowerBB'].shift(2) * (1 - buffer_percentage)), 1, 0
101
  )
102
-
103
- # Sell signal: Price above UpperBB for 2 consecutive periods with buffer
104
  df['BB_Signal'] = np.where(
105
  (df['Close'] > df['UpperBB'] * (1 + buffer_percentage)) &
106
  (df['Close'].shift(1) > df['UpperBB'].shift(1) * (1 + buffer_percentage)) &
107
  (df['Close'].shift(2) > df['UpperBB'].shift(2) * (1 + buffer_percentage)), -1, df['BB_Signal']
108
  )
109
-
110
 
111
- # Modified Stochastic (tighter thresholds)
112
  df['Stochastic_Signal'] = np.where((df['SlowK'] < 5) & (df['SlowD'] < 5), 1, 0)
113
  df['Stochastic_Signal'] = np.where((df['SlowK'] > 99) & (df['SlowD'] > 95), -1, df['Stochastic_Signal'])
114
 
115
- # Modified CMF Signals
116
- df['CMF_Signal'] = np.where(df['CMF'] > 0.4, -1, np.where(df['CMF'] < -0.4, 1, 0)) # Changed from 0.3 to 0.4
117
 
118
- # Modified CCI Signals
119
- df['CCI_Signal'] = np.where(df['CCI'] < -195, 1, 0) # Changed from -180
120
- df['CCI_Signal'] = np.where(df['CCI'] > 195, -1, df['CCI_Signal']) # Changed from 150
121
 
122
- # Include SMA and MACD in combined signals
123
  df['Combined_Signal'] = df[['RSI_Signal', 'BB_Signal',
124
  'Stochastic_Signal', 'CMF_Signal',
125
  'CCI_Signal', 'MACD_Signal']].sum(axis=1)
126
 
127
  return df
128
 
129
- # %%
130
- def plot_combined_signals(df, ticker):
131
- # Create a figure
132
  fig = go.Figure()
133
-
134
- # Add closing price trace
135
  fig.add_trace(go.Scatter(
136
- x=df.index, y=df['Close'],
 
137
  mode='lines',
138
- name='Closing Price',
139
- line=dict(color='indigo', width=2)
 
140
  ))
141
-
142
- # Add buy signals
143
- buy_signals = df[df['Combined_Signal'] >= 3]
144
  fig.add_trace(go.Scatter(
145
- x=buy_signals.index, y=buy_signals['Close'],
146
- mode='markers',
147
- marker=dict(symbol='triangle-up', size=10, color='lightgreen'),
148
- name='Buy Signal'
149
  ))
150
-
151
- # Add sell signals
152
- sell_signals = df[df['Combined_Signal'] <= -2]
153
  fig.add_trace(go.Scatter(
154
- x=sell_signals.index, y=sell_signals['Close'],
155
- mode='markers',
156
- marker=dict(symbol='triangle-down', size=10, color='red'),
157
- name='Sell Signal'
158
  ))
159
 
160
- # Combined signal trace
161
  fig.add_trace(go.Scatter(
162
- x=df.index, y=df['Combined_Signal'],
163
- mode='lines',
164
- name='Combined Signal',
165
- line=dict(color='lightgreen', width=0.5),
166
- yaxis='y2'
167
  ))
168
-
169
- # Update layout
170
- fig.update_layout(
171
- title=f'{ticker}: 360 Stock Price and Combined Trading Signal',
172
- xaxis=dict(title='Date'),
173
- yaxis=dict(title='Price', side='left'),
174
- yaxis2=dict(title='Combined Signal', overlaying='y', side='right', showgrid=False),
175
- plot_bgcolor='black',
176
- paper_bgcolor='black',
177
- font=dict(color='white')
178
- )
179
-
180
- return fig
181
-
182
-
183
-
184
-
185
- # %%
186
- def plot_individual_signals(df, ticker):
187
- # Create a figure
188
- fig = go.Figure()
189
  fig.add_trace(go.Scatter(
190
- x=df.index, y=df['Close'],
191
- mode='lines',
192
- name='Closing Price',
193
- line=dict(color='lightgreen', width=2)
 
 
 
194
  ))
195
 
196
- # Add buy/sell signals for each indicator
197
- signal_names = ['RSI_Signal', 'BB_Signal',
198
- 'Stochastic_Signal', 'CMF_Signal',
199
- 'CCI_Signal']
200
-
 
 
 
201
  for signal in signal_names:
202
- buy_signals = df[df[signal] == 1]
203
- sell_signals = df[df[signal] == -1]
204
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  fig.add_trace(go.Scatter(
206
- x=buy_signals.index, y=buy_signals['Close'],
 
207
  mode='markers',
208
- marker=dict(symbol='triangle-up', size=10, color='blackblue'),
209
- name=f'{signal} Buy Signal'
 
 
210
  ))
211
 
 
 
 
212
  fig.add_trace(go.Scatter(
213
- x=sell_signals.index, y=sell_signals['Close'],
 
214
  mode='markers',
215
- marker=dict(symbol='triangle-down', size=10, color='red'),
216
- name=f'{signal} Sell Signal'
 
 
217
  ))
218
 
 
219
  fig.update_layout(
220
- title=f'{ticker}: Individual Trading Signals',
221
- xaxis=dict(title='Date'),
222
- yaxis=dict(title='Price', side='left'),
223
- plot_bgcolor='black',
224
- paper_bgcolor='black',
225
- font=dict(color='white')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  )
227
-
228
- return fig
229
-
230
-
231
- def display_signals(df):
232
- # Create a signals DataFrame
233
- signals_df = df[['Close', 'SMA_Signal', 'MACD_Signal', 'RSI_Signal',
234
- 'BB_Signal', 'Stochastic_Signal',
235
- 'CMF_Signal', 'CCI_Signal']].copy()
236
-
237
- # The Date is the index, so we don't need to add it as a column
238
- signals_df.index.name = 'Date' # Name the index for better readability
239
-
240
- # Replace signal values with 'Buy', 'Sell', or 'Hold'
241
- for column in signals_df.columns:
242
- signals_df[column] = signals_df[column].replace(
243
- {1: 'Buy', -1: 'Sell', 0: 'Hold'}
244
  )
 
 
 
245
 
246
- return signals_df
247
-
248
- # Define the stock analysis function (keep only one version)
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
- # If the DataFrame has a MultiIndex for columns, handle it
254
- if isinstance(df.columns, pd.MultiIndex):
255
- df.columns = df.columns.droplevel(1)
256
-
257
- # Generate signals
258
- df = generate_trading_signals(df)
259
-
260
- # Last 360 days for plotting
261
- df_last_360 = df.tail(360)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
 
263
- # Plot combined signals
264
- fig_signals = plot_combined_signals(df_last_360, ticker)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
 
266
- # Plot individual signals
267
- fig_individual_signals = plot_individual_signals(df_last_360, ticker)
 
 
 
 
 
 
 
268
 
269
- # Create signals DataFrame for display
270
- signals_df = df_last_360[['Close', 'SMA_Signal', 'MACD_Signal', 'RSI_Signal',
271
- 'BB_Signal', 'Stochastic_Signal', 'CMF_Signal',
272
- 'CCI_Signal']].copy()
 
273
 
274
- # Format signals for display
275
- for column in signals_df.columns[1:]: # Skip 'Close'
276
- signals_df[column] = signals_df[column].replace({1: 'Buy', -1: 'Sell', 0: 'Hold'})
277
-
278
- return fig_individual_signals, signals_df
279
-
280
- # Define Gradio interface
281
- with gr.Blocks(theme=gr.themes.Monochrome()) as demo:
282
- gr.Markdown("## 360 Stock Market Analysis")
283
-
284
- ticker_input = gr.Textbox(label="Enter Stock Ticker (e.g., AAPL, NVDA)", value="NVDA")
285
- start_date_input = gr.Textbox(label="Start Date (YYYY-MM-DD)", value="2022-01-01")
286
- end_date_input = gr.Textbox(label="End Date (YYYY-MM-DD)", value="2023-01-01") # Fixed end date
287
-
288
- # Create a submit button that runs the stock analysis function
289
- button = gr.Button("Analyze Stock")
290
 
291
- # Outputs: Display results, charts
292
- individual_signals_output = gr.Plot(label="Trading Signals")
293
- signals_df_output = gr.Dataframe(label="Buy/Sell Signals")
294
-
295
- # Link button to function with both outputs
296
- button.click(stock_analysis,
297
- inputs=[ticker_input, start_date_input, end_date_input],
298
- outputs=[individual_signals_output,signals_df_output])
299
 
300
  # Launch the interface
301
- demo.launch()
 
 
 
1
  import gradio as gr
2
  import pandas as pd
3
  import yfinance as yf
 
4
  import plotly.graph_objects as go
5
  import numpy as np
6
 
7
+ # Functions for calculating indicators (keeping these unchanged)
 
8
  def calculate_sma(df, window):
9
  return df['Close'].rolling(window=window).mean()
10
 
11
  def calculate_ema(df, window):
12
  return df['Close'].ewm(span=window, adjust=False).mean()
13
 
 
14
  def calculate_macd(df):
15
  short_ema = df['Close'].ewm(span=12, adjust=False).mean()
16
  long_ema = df['Close'].ewm(span=26, adjust=False).mean()
 
18
  signal = macd.ewm(span=9, adjust=False).mean()
19
  return macd, signal
20
 
 
21
  def calculate_rsi(df):
22
  delta = df['Close'].diff()
23
  gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
 
39
  slowd = slowk.rolling(window=3).mean()
40
  return slowk, slowd
41
 
 
 
42
  def calculate_cmf(df, window=20):
43
  mfv = ((df['Close'] - df['Low']) - (df['High'] - df['Close'])) / (df['High'] - df['Low']) * df['Volume']
44
  cmf = mfv.rolling(window=window).sum() / df['Volume'].rolling(window=window).sum()
45
  return cmf
46
 
47
  def calculate_cci(df, window=20):
 
48
  typical_price = (df['High'] + df['Low'] + df['Close']) / 3
49
  sma = typical_price.rolling(window=window).mean()
50
  mean_deviation = (typical_price - sma).abs().rolling(window=window).mean()
51
  cci = (typical_price - sma) / (0.015 * mean_deviation)
52
  return cci
53
 
 
 
54
  def generate_trading_signals(df):
55
  # Calculate various indicators
56
  df['SMA_30'] = calculate_sma(df, 30)
 
63
  df['CMF'] = calculate_cmf(df)
64
  df['CCI'] = calculate_cci(df)
65
 
66
+ # Generate trading signals with stricter SMA threshold
67
+ # Making SMA threshold stricter - require 3% difference between SMAs
68
+ df['SMA_Diff_Pct'] = (df['SMA_30'] - df['SMA_100']) / df['SMA_100'] * 100
69
+ df['SMA_Signal'] = np.where(df['SMA_Diff_Pct'] > 30, 1, 0) # Buy when SMA_30 is 30% above SMA_100
70
+ df['SMA_Signal'] = np.where(df['SMA_Diff_Pct'] < -30, -1, df['SMA_Signal']) # Sell when SMA_30 is 30% below SMA_100
71
 
72
  macd, signal = calculate_macd(df)
73
+ df['MACD'] = macd
74
+ df['MACD_Signal_Line'] = signal
75
  df['MACD_Signal'] = np.select([(macd > signal) & (macd.shift(1) <= signal.shift(1)),
76
+ (macd < signal) & (macd.shift(1) >= signal.shift(1))], [1, -1], default=0)
 
 
 
 
 
77
 
78
+ # RSI Signals
79
+ df['RSI_Signal'] = np.where(df['RSI'] < 12, 1, 0)
80
+ df['RSI_Signal'] = np.where(df['RSI'] > 95, -1, df['RSI_Signal'])
81
 
82
+ # Bollinger Bands with buffer
 
83
  buffer_percentage = 0.01 # 1% buffer
 
 
84
  df['BB_Signal'] = np.where(
85
  (df['Close'] < df['LowerBB'] * (1 - buffer_percentage)) &
86
  (df['Close'].shift(1) < df['LowerBB'].shift(1) * (1 - buffer_percentage)) &
87
  (df['Close'].shift(2) < df['LowerBB'].shift(2) * (1 - buffer_percentage)), 1, 0
88
  )
 
 
89
  df['BB_Signal'] = np.where(
90
  (df['Close'] > df['UpperBB'] * (1 + buffer_percentage)) &
91
  (df['Close'].shift(1) > df['UpperBB'].shift(1) * (1 + buffer_percentage)) &
92
  (df['Close'].shift(2) > df['UpperBB'].shift(2) * (1 + buffer_percentage)), -1, df['BB_Signal']
93
  )
 
94
 
95
+ # Stochastic signals
96
  df['Stochastic_Signal'] = np.where((df['SlowK'] < 5) & (df['SlowD'] < 5), 1, 0)
97
  df['Stochastic_Signal'] = np.where((df['SlowK'] > 99) & (df['SlowD'] > 95), -1, df['Stochastic_Signal'])
98
 
99
+ # CMF Signals
100
+ df['CMF_Signal'] = np.where(df['CMF'] > 0.4, -1, np.where(df['CMF'] < -0.4, 1, 0))
101
 
102
+ # CCI Signals
103
+ df['CCI_Signal'] = np.where(df['CCI'] < -195, 1, 0)
104
+ df['CCI_Signal'] = np.where(df['CCI'] > 195, -1, df['CCI_Signal'])
105
 
106
+ # Combined signal (keeping for reference but not used in the output)
107
  df['Combined_Signal'] = df[['RSI_Signal', 'BB_Signal',
108
  'Stochastic_Signal', 'CMF_Signal',
109
  'CCI_Signal', 'MACD_Signal']].sum(axis=1)
110
 
111
  return df
112
 
113
+ def plot_simplified_signals(df, ticker):
114
+ # Create a figure with improved styling
 
115
  fig = go.Figure()
116
+
117
+ # Use a line chart instead of candlestick for simplicity
118
  fig.add_trace(go.Scatter(
119
+ x=df.index,
120
+ y=df['Close'],
121
  mode='lines',
122
+ name='Price',
123
+ line=dict(color='#26a69a', width=2),
124
+ opacity=0.9
125
  ))
126
+
127
+ # Add SMA lines
 
128
  fig.add_trace(go.Scatter(
129
+ x=df.index, y=df['SMA_30'],
130
+ mode='lines',
131
+ name='SMA 30',
132
+ line=dict(color='#42a5f5', width=1.5, dash='dot')
133
  ))
134
+
 
 
135
  fig.add_trace(go.Scatter(
136
+ x=df.index, y=df['SMA_100'],
137
+ mode='lines',
138
+ name='SMA 100',
139
+ line=dict(color='#5e35b1', width=1.5, dash='dot')
140
  ))
141
 
142
+ # Add bollinger bands with lighter appearance
143
  fig.add_trace(go.Scatter(
144
+ x=df.index, y=df['UpperBB'],
145
+ mode='lines',
146
+ name='Upper BB',
147
+ line=dict(color='rgba(250, 250, 250, 0.3)', width=1),
148
+ showlegend=True
149
  ))
150
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  fig.add_trace(go.Scatter(
152
+ x=df.index, y=df['LowerBB'],
153
+ mode='lines',
154
+ name='Lower BB',
155
+ line=dict(color='rgba(250, 250, 250, 0.3)', width=1),
156
+ fill='tonexty',
157
+ fillcolor='rgba(173, 216, 230, 0.1)',
158
+ showlegend=True
159
  ))
160
 
161
+ # Group signals by type to reduce legend clutter
162
+ buy_signals_df = pd.DataFrame(index=df.index)
163
+ sell_signals_df = pd.DataFrame(index=df.index)
164
+
165
+ signal_names = ['RSI_Signal', 'BB_Signal', 'Stochastic_Signal',
166
+ 'CMF_Signal', 'CCI_Signal', 'MACD_Signal', 'SMA_Signal']
167
+
168
+ # Collect all buy and sell signals
169
  for signal in signal_names:
170
+ buy_signals_df[signal] = np.where(df[signal] == 1, df['Close'], np.nan)
171
+ sell_signals_df[signal] = np.where(df[signal] == -1, df['Close'], np.nan)
172
+
173
+ # Add hover data
174
+ buy_hovers = []
175
+ for idx in buy_signals_df.index:
176
+ signals_on_day = [col.split('_')[0] for col in buy_signals_df.columns
177
+ if not pd.isna(buy_signals_df.loc[idx, col])]
178
+ if signals_on_day:
179
+ hover_text = f"Buy Signals: {', '.join(signals_on_day)}<br>Date: {idx.strftime('%Y-%m-%d')}<br>Price: ${df.loc[idx, 'Close']:.2f}"
180
+ buy_hovers.append((idx, df.loc[idx, 'Close'], hover_text))
181
+
182
+ sell_hovers = []
183
+ for idx in sell_signals_df.index:
184
+ signals_on_day = [col.split('_')[0] for col in sell_signals_df.columns
185
+ if not pd.isna(sell_signals_df.loc[idx, col])]
186
+ if signals_on_day:
187
+ hover_text = f"Sell Signals: {', '.join(signals_on_day)}<br>Date: {idx.strftime('%Y-%m-%d')}<br>Price: ${df.loc[idx, 'Close']:.2f}"
188
+ sell_hovers.append((idx, df.loc[idx, 'Close'], hover_text))
189
+
190
+ # Add buy signals (single trace for all buy signals)
191
+ if buy_hovers:
192
+ buy_x, buy_y, buy_texts = zip(*buy_hovers)
193
  fig.add_trace(go.Scatter(
194
+ x=buy_x,
195
+ y=[y * 0.995 for y in buy_y], # Position slightly below price for visibility
196
  mode='markers',
197
+ marker=dict(symbol='triangle-up', size=10, color='#00e676', line=dict(color='white', width=1)),
198
+ name='Buy Signals',
199
+ hoverinfo='text',
200
+ hovertext=buy_texts
201
  ))
202
 
203
+ # Add sell signals (single trace for all sell signals)
204
+ if sell_hovers:
205
+ sell_x, sell_y, sell_texts = zip(*sell_hovers)
206
  fig.add_trace(go.Scatter(
207
+ x=sell_x,
208
+ y=[y * 1.005 for y in sell_y], # Position slightly above price for visibility
209
  mode='markers',
210
+ marker=dict(symbol='triangle-down', size=10, color='#ff5252', line=dict(color='white', width=1)),
211
+ name='Sell Signals',
212
+ hoverinfo='text',
213
+ hovertext=sell_texts
214
  ))
215
 
216
+ # Improve the layout
217
  fig.update_layout(
218
+ title=dict(
219
+ text=f'{ticker}: Technical Analysis & Trading Signals',
220
+ font=dict(size=24, color='white'),
221
+ x=0.5
222
+ ),
223
+ xaxis=dict(
224
+ title='Date',
225
+ gridcolor='rgba(255, 255, 255, 0.1)',
226
+ linecolor='rgba(255, 255, 255, 0.2)'
227
+ ),
228
+ yaxis=dict(
229
+ title='Price',
230
+ side='right',
231
+ gridcolor='rgba(255, 255, 255, 0.1)',
232
+ linecolor='rgba(255, 255, 255, 0.2)',
233
+ tickprefix='$'
234
+ ),
235
+ plot_bgcolor='#1e1e1e',
236
+ paper_bgcolor='#1e1e1e',
237
+ font=dict(color='white'),
238
+ hovermode='closest',
239
+ legend=dict(
240
+ bgcolor='rgba(30, 30, 30, 0.8)',
241
+ bordercolor='rgba(255, 255, 255, 0.2)',
242
+ borderwidth=1,
243
+ font=dict(color='white', size=10),
244
+ orientation='h',
245
+ yanchor='bottom',
246
+ y=1.02,
247
+ xanchor='center',
248
+ x=0.5
249
+ ),
250
+ margin=dict(l=50, r=50, b=100, t=100, pad=4)
251
  )
252
+
253
+ # Add range selector for better time navigation
254
+ fig.update_xaxes(
255
+ rangeslider_visible=True,
256
+ rangeselector=dict(
257
+ buttons=list([
258
+ dict(count=1, label="1m", step="month", stepmode="backward"),
259
+ dict(count=3, label="3m", step="month", stepmode="backward"),
260
+ dict(count=6, label="6m", step="month", stepmode="backward"),
261
+ dict(count=1, label="YTD", step="year", stepmode="todate"),
262
+ dict(count=1, label="1y", step="year", stepmode="backward"),
263
+ dict(step="all")
264
+ ]),
265
+ bgcolor='rgba(30, 30, 30, 0.8)',
266
+ activecolor='#536dfe',
267
+ font=dict(color='white')
 
268
  )
269
+ )
270
+
271
+ return fig
272
 
 
 
 
273
  def stock_analysis(ticker, start_date, end_date):
274
+ try:
275
+ # Download stock data from Yahoo Finance
276
+ df = yf.download(ticker, start=start_date, end=end_date)
277
+
278
+ # Check if data was retrieved
279
+ if df.empty:
280
+ fig = go.Figure()
281
+ fig.add_annotation(
282
+ text="No data found for this ticker and date range",
283
+ xref="paper", yref="paper",
284
+ x=0.5, y=0.5,
285
+ showarrow=False,
286
+ font=dict(color="white", size=16)
287
+ )
288
+ fig.update_layout(
289
+ plot_bgcolor='#1e1e1e',
290
+ paper_bgcolor='#1e1e1e'
291
+ )
292
+ return fig
293
+
294
+ # If the DataFrame has a MultiIndex for columns, handle it
295
+ if isinstance(df.columns, pd.MultiIndex):
296
+ df.columns = df.columns.droplevel(1) if len(df.columns.levels) > 1 else df.columns
297
+
298
+ # Generate signals
299
+ df = generate_trading_signals(df)
300
+
301
+ # Last 360 days for plotting (or all data if less than 360 days)
302
+ df_last_360 = df.tail(min(360, len(df)))
303
 
304
+ # Plot simplified signals
305
+ fig_individual = plot_simplified_signals(df_last_360, ticker)
306
+
307
+ return fig_individual
308
+
309
+ except Exception as e:
310
+ # Create error figure
311
+ fig = go.Figure()
312
+ fig.add_annotation(
313
+ text=f"Error: {str(e)}",
314
+ xref="paper", yref="paper",
315
+ x=0.5, y=0.5,
316
+ showarrow=False,
317
+ font=dict(color="#ff5252", size=16)
318
+ )
319
+ fig.update_layout(
320
+ plot_bgcolor='#1e1e1e',
321
+ paper_bgcolor='#1e1e1e',
322
+ font=dict(color='white')
323
+ )
324
+ return fig
325
+
326
+ # Define Gradio interface with improved styling
327
+ custom_theme = gr.themes.Monochrome(
328
+ primary_hue="blue",
329
+ secondary_hue="purple",
330
+ neutral_hue="gray",
331
+ radius_size=gr.themes.sizes.radius_sm,
332
+ font=[gr.themes.GoogleFont("Inter"), "system-ui", "sans-serif"],
333
+ )
334
+
335
+ with gr.Blocks(theme=custom_theme) as demo:
336
+ gr.Markdown("# Simplified Stock Market Signal Analysis")
337
+ gr.Markdown("This app analyzes stock data and visualizes trading signals based on multiple technical indicators with a clean, simplified display.")
338
+
339
+ with gr.Row():
340
+ with gr.Column(scale=1):
341
+ ticker_input = gr.Textbox(
342
+ label="Stock Ticker Symbol",
343
+ placeholder="e.g., AAPL, NVDA, MSFT",
344
+ value="NVDA"
345
+ )
346
+ start_date_input = gr.Textbox(
347
+ label="Start Date",
348
+ placeholder="YYYY-MM-DD",
349
+ value="2022-01-01"
350
+ )
351
+ end_date_input = gr.Textbox(
352
+ label="End Date",
353
+ placeholder="YYYY-MM-DD",
354
+ value="2026-01-01"
355
+ )
356
+
357
+ # Create a submit button with styling
358
+ button = gr.Button("Analyze Stock", variant="primary")
359
 
360
+ # Output: Signals plot
361
+ signals_output = gr.Plot(label="Technical Analysis & Trading Signals")
362
+
363
+ # Link button to function
364
+ button.click(
365
+ stock_analysis,
366
+ inputs=[ticker_input, start_date_input, end_date_input],
367
+ outputs=[signals_output]
368
+ )
369
 
370
+ gr.Markdown("""
371
+ ## 📈 Trading Signals Legend
372
+ - **Green Triangle Up (▲)** indicates Buy signals
373
+ - **Red Triangle Down ()** indicates Sell signals
374
+ - Hover over signals to see which indicators triggered them
375
 
376
+ ## 🔍 Indicators & Thresholds
377
+ - **SMA**: Simple Moving Average (30 & 100 days) - 30% threshold
378
+ - **MACD**: Moving Average Convergence Divergence (12, 26, 9)
379
+ - **RSI**: Relative Strength Index (Buy < 12, Sell > 95)
380
+ - **BB**: Bollinger Bands (with 1% buffer)
381
+ - **Stochastic**: Stochastic Oscillator (Buy < 5, Sell > 99)
382
+ - **CMF**: Chaikin Money Flow (Buy < -0.4, Sell > 0.4)
383
+ - **CCI**: Commodity Channel Index (Buy < -195, Sell > 195)
 
 
 
 
 
 
 
 
384
 
385
+ ## 💡 Visualization Improvements
386
+ - Simple line chart for price
387
+ - Consolidated buy/sell signals
388
+ - Reduced visual clutter
389
+ - Enhanced hover information
390
+ - Interactive time range selection
391
+ """)
 
392
 
393
  # Launch the interface
394
+ demo.launch()