JayLacoma commited on
Commit
79ec766
·
verified ·
1 Parent(s): 1d1e622

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +195 -87
app.py CHANGED
@@ -4,7 +4,7 @@ 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
 
@@ -51,7 +51,51 @@ def calculate_cci(df, window=20):
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)
57
  df['SMA_100'] = calculate_sma(df, 100)
@@ -63,54 +107,71 @@ def generate_trading_signals(df):
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
 
@@ -140,30 +201,30 @@ def plot_simplified_signals(df, ticker):
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:
@@ -213,7 +274,7 @@ def plot_simplified_signals(df, ticker):
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',
@@ -247,7 +308,9 @@ def plot_simplified_signals(df, ticker):
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
@@ -270,7 +333,10 @@ def plot_simplified_signals(df, ticker):
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)
@@ -287,7 +353,9 @@ def stock_analysis(ticker, start_date, end_date):
287
  )
288
  fig.update_layout(
289
  plot_bgcolor='#1e1e1e',
290
- paper_bgcolor='#1e1e1e'
 
 
291
  )
292
  return fig
293
 
@@ -295,16 +363,33 @@ def stock_analysis(ticker, start_date, end_date):
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
@@ -319,7 +404,9 @@ def stock_analysis(ticker, start_date, end_date):
319
  fig.update_layout(
320
  plot_bgcolor='#1e1e1e',
321
  paper_bgcolor='#1e1e1e',
322
- font=dict(color='white')
 
 
323
  )
324
  return fig
325
 
@@ -333,8 +420,8 @@ custom_theme = gr.themes.Monochrome(
333
  )
334
 
335
  with gr.Blocks(theme=custom_theme) as demo:
336
- gr.Markdown("# 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):
@@ -351,19 +438,45 @@ with gr.Blocks(theme=custom_theme) as demo:
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
 
@@ -373,21 +486,16 @@ with gr.Blocks(theme=custom_theme) as demo:
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
 
4
  import plotly.graph_objects as go
5
  import numpy as np
6
 
7
+ # Functions for calculating indicators
8
  def calculate_sma(df, window):
9
  return df['Close'].rolling(window=window).mean()
10
 
 
51
  cci = (typical_price - sma) / (0.015 * mean_deviation)
52
  return cci
53
 
54
+ # Function to adjust thresholds based on sensitivity
55
+ def adjust_thresholds_by_sensitivity(sensitivity):
56
+ """
57
+ Convert a single sensitivity value (1-10) to appropriate thresholds
58
+ 1 = Most sensitive (more signals)
59
+ 10 = Least sensitive (fewer, stronger signals)
60
+ """
61
+ # Map sensitivity to thresholds
62
+ if sensitivity == 1: # Most sensitive
63
+ return {
64
+ 'SMA': 5,
65
+ 'RSI_lower': 30,
66
+ 'RSI_upper': 70,
67
+ 'BB': 0.5,
68
+ 'Stochastic_lower': 20,
69
+ 'Stochastic_upper': 80,
70
+ 'CMF': 0.1,
71
+ 'CCI': 100
72
+ }
73
+ elif sensitivity == 10: # Least sensitive
74
+ return {
75
+ 'SMA': 50,
76
+ 'RSI_lower': 5,
77
+ 'RSI_upper': 95,
78
+ 'BB': 5,
79
+ 'Stochastic_lower': 5,
80
+ 'Stochastic_upper': 95,
81
+ 'CMF': 0.6,
82
+ 'CCI': 300
83
+ }
84
+ else:
85
+ # Linear interpolation between extremes
86
+ factor = (sensitivity - 1) / 9 # 0 to 1
87
+ return {
88
+ 'SMA': int(5 + (50 - 5) * factor),
89
+ 'RSI_lower': int(30 - (30 - 5) * factor),
90
+ 'RSI_upper': int(70 + (95 - 70) * factor),
91
+ 'BB': 0.5 + (5 - 0.5) * factor,
92
+ 'Stochastic_lower': int(20 - (20 - 5) * factor),
93
+ 'Stochastic_upper': int(80 + (95 - 80) * factor),
94
+ 'CMF': 0.1 + (0.6 - 0.1) * factor,
95
+ 'CCI': int(100 + (300 - 100) * factor)
96
+ }
97
+
98
+ def generate_trading_signals(df, thresholds, enabled_signals):
99
  # Calculate various indicators
100
  df['SMA_30'] = calculate_sma(df, 30)
101
  df['SMA_100'] = calculate_sma(df, 100)
 
107
  df['CMF'] = calculate_cmf(df)
108
  df['CCI'] = calculate_cci(df)
109
 
110
+ # Initialize all signals as 0 (no signal)
111
+ signal_columns = ['SMA_Signal', 'MACD_Signal', 'RSI_Signal', 'BB_Signal',
112
+ 'Stochastic_Signal', 'CMF_Signal', 'CCI_Signal']
113
+ for col in signal_columns:
114
+ df[col] = 0
115
+
116
+ # Only generate signals for enabled indicators
117
+
118
+ # SMA Signal
119
+ if 'SMA' in enabled_signals:
120
+ sma_threshold = thresholds['SMA']
121
+ df['SMA_Diff_Pct'] = (df['SMA_30'] - df['SMA_100']) / df['SMA_100'] * 100
122
+ df['SMA_Signal'] = np.where(df['SMA_Diff_Pct'] > sma_threshold, 1, 0)
123
+ df['SMA_Signal'] = np.where(df['SMA_Diff_Pct'] < -sma_threshold, -1, df['SMA_Signal'])
124
+
125
+ # MACD Signal
126
+ if 'MACD' in enabled_signals:
127
+ macd, signal = calculate_macd(df)
128
+ df['MACD'] = macd
129
+ df['MACD_Signal_Line'] = signal
130
+ df['MACD_Signal'] = np.select([(macd > signal) & (macd.shift(1) <= signal.shift(1)),
131
+ (macd < signal) & (macd.shift(1) >= signal.shift(1))], [1, -1], default=0)
132
 
 
 
 
 
 
 
133
  # RSI Signals
134
+ if 'RSI' in enabled_signals:
135
+ rsi_lower = thresholds['RSI_lower']
136
+ rsi_upper = thresholds['RSI_upper']
137
+ df['RSI_Signal'] = np.where(df['RSI'] < rsi_lower, 1, 0)
138
+ df['RSI_Signal'] = np.where(df['RSI'] > rsi_upper, -1, df['RSI_Signal'])
139
 
140
+ # Bollinger Bands
141
+ if 'BB' in enabled_signals:
142
+ bb_buffer = thresholds['BB'] / 100 # Convert percentage to decimal
143
+ df['BB_Signal'] = np.where(
144
+ (df['Close'] < df['LowerBB'] * (1 - bb_buffer)) &
145
+ (df['Close'].shift(1) < df['LowerBB'].shift(1) * (1 - bb_buffer)) &
146
+ (df['Close'].shift(2) < df['LowerBB'].shift(2) * (1 - bb_buffer)), 1, 0
147
+ )
148
+ df['BB_Signal'] = np.where(
149
+ (df['Close'] > df['UpperBB'] * (1 + bb_buffer)) &
150
+ (df['Close'].shift(1) > df['UpperBB'].shift(1) * (1 + bb_buffer)) &
151
+ (df['Close'].shift(2) > df['UpperBB'].shift(2) * (1 + bb_buffer)), -1, df['BB_Signal']
152
+ )
153
 
154
  # Stochastic signals
155
+ if 'Stochastic' in enabled_signals:
156
+ stoch_lower = thresholds['Stochastic_lower']
157
+ stoch_upper = thresholds['Stochastic_upper']
158
+ df['Stochastic_Signal'] = np.where((df['SlowK'] < stoch_lower) & (df['SlowD'] < stoch_lower), 1, 0)
159
+ df['Stochastic_Signal'] = np.where((df['SlowK'] > stoch_upper) & (df['SlowD'] > stoch_upper), -1, df['Stochastic_Signal'])
160
+
161
  # CMF Signals
162
+ if 'CMF' in enabled_signals:
163
+ cmf_threshold = thresholds['CMF']
164
+ df['CMF_Signal'] = np.where(df['CMF'] > cmf_threshold, -1, np.where(df['CMF'] < -cmf_threshold, 1, 0))
165
+
166
  # CCI Signals
167
+ if 'CCI' in enabled_signals:
168
+ cci_threshold = thresholds['CCI']
169
+ df['CCI_Signal'] = np.where(df['CCI'] < -cci_threshold, 1, 0)
170
+ df['CCI_Signal'] = np.where(df['CCI'] > cci_threshold, -1, df['CCI_Signal'])
 
 
 
171
 
172
  return df
173
 
174
+ def plot_simplified_signals(df, ticker, enabled_signals):
175
  # Create a figure with improved styling
176
  fig = go.Figure()
177
 
 
201
  ))
202
 
203
  # Add bollinger bands with lighter appearance
204
+ if 'BB' in enabled_signals:
205
+ fig.add_trace(go.Scatter(
206
+ x=df.index, y=df['UpperBB'],
207
+ mode='lines',
208
+ name='Upper BB',
209
+ line=dict(color='rgba(250, 250, 250, 0.3)', width=1),
210
+ showlegend=True
211
+ ))
212
+
213
+ fig.add_trace(go.Scatter(
214
+ x=df.index, y=df['LowerBB'],
215
+ mode='lines',
216
+ name='Lower BB',
217
+ line=dict(color='rgba(250, 250, 250, 0.3)', width=1),
218
+ fill='tonexty',
219
+ fillcolor='rgba(173, 216, 230, 0.1)',
220
+ showlegend=True
221
+ ))
222
 
223
  # Group signals by type to reduce legend clutter
224
  buy_signals_df = pd.DataFrame(index=df.index)
225
  sell_signals_df = pd.DataFrame(index=df.index)
226
 
227
+ signal_names = [f"{signal}_Signal" for signal in enabled_signals]
 
228
 
229
  # Collect all buy and sell signals
230
  for signal in signal_names:
 
274
  hovertext=sell_texts
275
  ))
276
 
277
+ # Improve the layout with larger dimensions
278
  fig.update_layout(
279
  title=dict(
280
  text=f'{ticker}: Technical Analysis & Trading Signals',
 
308
  xanchor='center',
309
  x=0.5
310
  ),
311
+ margin=dict(l=50, r=50, b=100, t=100, pad=4),
312
+ height=800, # Increased height
313
+ width=1200 # Increased width
314
  )
315
 
316
  # Add range selector for better time navigation
 
333
 
334
  return fig
335
 
336
+ def stock_analysis(ticker, start_date, end_date,
337
+ sensitivity, # New simplified parameter
338
+ use_sma, use_macd, use_rsi, use_bb,
339
+ use_stoch, use_cmf, use_cci):
340
  try:
341
  # Download stock data from Yahoo Finance
342
  df = yf.download(ticker, start=start_date, end=end_date)
 
353
  )
354
  fig.update_layout(
355
  plot_bgcolor='#1e1e1e',
356
+ paper_bgcolor='#1e1e1e',
357
+ height=800,
358
+ width=1200
359
  )
360
  return fig
361
 
 
363
  if isinstance(df.columns, pd.MultiIndex):
364
  df.columns = df.columns.droplevel(1) if len(df.columns.levels) > 1 else df.columns
365
 
366
+ # Create list of enabled signals
367
+ enabled_signals = []
368
+ if use_sma: enabled_signals.append('SMA')
369
+ if use_macd: enabled_signals.append('MACD')
370
+ if use_rsi: enabled_signals.append('RSI')
371
+ if use_bb: enabled_signals.append('BB')
372
+ if use_stoch: enabled_signals.append('Stochastic')
373
+ if use_cmf: enabled_signals.append('CMF')
374
+ if use_cci: enabled_signals.append('CCI')
375
+
376
+ # If no signals are enabled, enable all by default
377
+ if not enabled_signals:
378
+ enabled_signals = ['SMA', 'MACD', 'RSI', 'BB', 'Stochastic', 'CMF', 'CCI']
379
+
380
+ # Get thresholds from sensitivity
381
+ thresholds = adjust_thresholds_by_sensitivity(sensitivity)
382
+
383
  # Generate signals
384
+ df = generate_trading_signals(df, thresholds, enabled_signals)
385
 
386
  # Last 360 days for plotting (or all data if less than 360 days)
387
  df_last_360 = df.tail(min(360, len(df)))
388
 
389
  # Plot simplified signals
390
+ fig = plot_simplified_signals(df_last_360, ticker, enabled_signals)
391
 
392
+ return fig
393
 
394
  except Exception as e:
395
  # Create error figure
 
404
  fig.update_layout(
405
  plot_bgcolor='#1e1e1e',
406
  paper_bgcolor='#1e1e1e',
407
+ font=dict(color='white'),
408
+ height=800,
409
+ width=1200
410
  )
411
  return fig
412
 
 
420
  )
421
 
422
  with gr.Blocks(theme=custom_theme) as demo:
423
+ gr.Markdown("# Stock Market Signal Analysis")
424
+ gr.Markdown("This app helps you analyze stocks with technical indicators and generates trading signals.")
425
 
426
  with gr.Row():
427
  with gr.Column(scale=1):
 
438
  end_date_input = gr.Textbox(
439
  label="End Date",
440
  placeholder="YYYY-MM-DD",
441
+ value="2025-03-10" # Updated to current date
442
  )
443
 
444
+ gr.Markdown("### Choose Indicators")
445
+ with gr.Row():
446
+ use_sma = gr.Checkbox(label="SMA", value=True)
447
+ use_macd = gr.Checkbox(label="MACD", value=True)
448
+ use_rsi = gr.Checkbox(label="RSI", value=True)
449
+ use_bb = gr.Checkbox(label="Bollinger", value=True)
450
+ use_stoch = gr.Checkbox(label="Stochastic", value=True)
451
+ use_cmf = gr.Checkbox(label="CMF", value=True)
452
+ use_cci = gr.Checkbox(label="CCI", value=True)
453
+
454
+ gr.Markdown("### Signal Sensitivity")
455
+ with gr.Row():
456
+ sensitivity = gr.Slider(
457
+ label="Signal Sensitivity",
458
+ minimum=1,
459
+ maximum=10,
460
+ step=1,
461
+ value=5,
462
+ info="1 = (sensitive), 10 = (strict)"
463
+ )
464
+
465
  # Create a submit button with styling
466
  button = gr.Button("Analyze Stock", variant="primary")
467
 
468
+ # Output: Signals plot with increased height
469
  signals_output = gr.Plot(label="Technical Analysis & Trading Signals")
470
 
471
+ # Link button to function with updated parameters
472
  button.click(
473
  stock_analysis,
474
+ inputs=[
475
+ ticker_input, start_date_input, end_date_input,
476
+ sensitivity, # Single threshold parameter
477
+ use_sma, use_macd, use_rsi, use_bb,
478
+ use_stoch, use_cmf, use_cci
479
+ ],
480
  outputs=[signals_output]
481
  )
482
 
 
486
  - **Red Triangle Down (▼)** indicates Sell signals
487
  - Hover over signals to see which indicators triggered them
488
 
489
+ ## 🔍 Signal Sensitivity Explained
490
+ - **Lower values (1-3)**: More frequent signals, good for short-term trading
491
+ - **Medium values (4-6)**: Balanced approach, moderate number of signals
492
+ - **Higher values (7-10)**: Fewer but potentially stronger signals, good for long-term investors
 
 
 
 
493
 
494
+ ## 🛠️ Trading Strategy Tips
495
+ - **Day Trading**: Use lower sensitivity with multiple indicators
496
+ - **Swing Trading**: Use medium sensitivity with 3-4 indicators
497
+ - **Long-term Investing**: Use higher sensitivity focusing on trend indicators
498
+ - **Combine**: Using multiple indicators helps confirm signals and reduce false positives
 
499
  """)
500
 
501
  # Launch the interface