GLAkavya commited on
Commit
d9028d3
Β·
verified Β·
1 Parent(s): 937d618

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +131 -235
app.py CHANGED
@@ -7,53 +7,68 @@ import yfinance as yf
7
  import warnings
8
  warnings.filterwarnings('ignore')
9
 
10
- class SimpleRiskApp:
11
  def __init__(self):
12
- # Only 10 reliable stocks with correct names
13
- self.available_stocks = [
14
- 'AAPL', 'MSFT', 'GOOGL', 'AMZN', 'TSLA',
15
- 'META', 'NVDA', 'NFLX', 'JPM', 'JNJ'
 
 
 
 
 
 
 
 
16
  ]
17
- print("🎯 Simple Portfolio Risk Analyzer Ready!")
 
18
 
19
- def get_stock_data(self, symbols, period='2y'):
20
- """Get stock data with error handling"""
21
  try:
 
 
 
22
  data = yf.download(symbols, period=period, progress=False)['Adj Close']
23
- return data
 
 
24
  except Exception as e:
25
  print(f"❌ Data fetch error: {str(e)}")
26
- return None
27
-
28
- def calculate_portfolio_metrics(self, selected_stocks, days=252, simulations=5000):
29
- """Calculate metrics for selected stocks"""
30
- if not selected_stocks:
31
  return None, None
 
 
 
 
 
32
 
33
  try:
34
- print(f"πŸ“Š Analyzing: {selected_stocks}")
35
-
36
- # Get fresh data
37
- data = self.get_stock_data(selected_stocks)
38
- if data is None or data.empty:
39
- return None, None
40
 
41
  # Calculate returns
42
  returns = data.pct_change().dropna()
43
- n_stocks = len(selected_stocks)
 
44
 
45
- # Equal weights
46
  weights = np.array([1/n_stocks] * n_stocks)
47
 
48
  # Calculate parameters
49
  mean_returns = returns.mean().values
50
  cov_matrix = returns.cov().values
51
 
52
- # Ensure covariance matrix is positive definite
53
  min_eig = np.min(np.real(np.linalg.eigvals(cov_matrix)))
54
  if min_eig < 0:
55
  cov_matrix -= 10 * min_eig * np.eye(*cov_matrix.shape)
56
 
 
 
57
  # Generate simulation
58
  np.random.seed(42)
59
  L = np.linalg.cholesky(cov_matrix)
@@ -61,11 +76,9 @@ class SimpleRiskApp:
61
  simulation_results = []
62
 
63
  for i in range(simulations):
64
- # Generate correlated returns
65
  random_numbers = np.random.normal(0, 1, size=(days, n_stocks))
66
  correlated_returns = random_numbers @ L.T + mean_returns
67
 
68
- # Calculate portfolio path
69
  portfolio_value = 100.0
70
  portfolio_path = [portfolio_value]
71
 
@@ -84,264 +97,147 @@ class SimpleRiskApp:
84
  'expected_value': float(np.mean(final_values)),
85
  'prob_10_loss': float(np.mean(final_values < 90)),
86
  'prob_20_loss': float(np.mean(final_values < 80)),
87
- 'prob_30_loss': float(np.mean(final_values < 70)),
88
  'var_95': float(np.percentile(final_values, 5)),
89
  'best_case': float(np.percentile(final_values, 95)),
90
- 'num_stocks': n_stocks,
91
  'weights': [f"{(1/n_stocks)*100:.1f}%" for _ in range(n_stocks)]
92
  }
93
 
94
- print(f"βœ… Analysis completed for {selected_stocks}")
95
- return metrics, simulation_array
96
 
97
  except Exception as e:
98
  print(f"❌ Analysis error: {str(e)}")
99
- return None, None
100
 
101
- def create_simulation_plot(self, simulation_results, stock_names):
102
- """Create simulation plot"""
103
- if simulation_results is None:
104
- return None
105
-
106
- fig = go.Figure()
107
-
108
- # Sample paths
109
- for i in range(min(30, len(simulation_results))):
110
- fig.add_trace(go.Scatter(
111
- y=simulation_results[i],
112
- mode='lines',
113
- line=dict(width=1, color='lightblue'),
114
- opacity=0.2,
115
- showlegend=False
116
  ))
117
 
118
- # Mean path
119
- mean_path = np.mean(simulation_results, axis=0)
120
- fig.add_trace(go.Scatter(
121
- y=mean_path,
122
- mode='lines',
123
- line=dict(width=3, color='red'),
124
- name='Average Path'
125
- ))
126
-
127
- stock_label = stock_names[0] if len(stock_names) == 1 else f"{len(stock_names)}-Stock Portfolio"
128
- fig.update_layout(
129
- title=f"πŸ“ˆ Monte Carlo Simulation - {stock_label}",
130
- xaxis_title="Trading Days",
131
- yaxis_title="Portfolio Value ($)",
132
- height=400
133
- )
134
-
135
- return fig
136
-
137
- def create_distribution_plot(self, simulation_results, metrics, stock_names):
138
- """Create distribution plot"""
139
- if simulation_results is None or metrics is None:
140
- return None
141
-
142
- final_values = simulation_results[:, -1]
143
 
144
- stock_label = stock_names[0] if len(stock_names) == 1 else f"{len(stock_names)}-Stock Portfolio"
145
- fig = px.histogram(
146
- x=final_values,
147
- nbins=50,
148
- title=f"πŸ“Š Final Value Distribution - {stock_label}",
149
- color_discrete_sequence=['#667eea']
150
- )
151
 
152
- fig.add_vline(x=metrics['expected_value'], line_dash="dash",
153
- line_color="red", annotation_text=f"Mean: ${metrics['expected_value']:.2f}")
154
- fig.add_vline(x=metrics['var_95'], line_dash="dash",
155
- line_color="orange", annotation_text=f"5% VaR: ${metrics['var_95']:.2f}")
156
- fig.update_layout(height=400, showlegend=False)
157
-
158
- return fig
159
-
160
- def create_risk_gauge(self, metrics):
161
- """Create risk gauge"""
162
- if metrics is None:
163
- return None
164
 
 
165
  risk_prob = metrics['prob_10_loss'] * 100
166
-
167
- fig = go.Figure(go.Indicator(
168
- mode="gauge+number",
169
- value=risk_prob,
170
- domain={'x': [0, 1], 'y': [0, 1]},
171
- title={'text': "Probability of >10% Loss"},
172
- gauge={
173
- 'axis': {'range': [0, 50]},
174
- 'bar': {'color': "darkblue"},
175
- 'steps': [
176
- {'range': [0, 15], 'color': "lightgreen"},
177
- {'range': [15, 30], 'color': "yellow"},
178
- {'range': [30, 50], 'color': "red"}
179
- ]
180
- }
181
  ))
182
- fig.update_layout(height=300)
183
- return fig
184
-
185
- def analyze_portfolio(self, selected_stocks, simulation_days, num_simulations):
186
- """Main analysis function"""
187
- print(f"πŸ” Analyzing: {selected_stocks}")
188
-
189
- if not selected_stocks:
190
- return self.create_error_html("Please select at least one stock"), None, None, None
191
 
192
- try:
193
- metrics, simulations = self.calculate_portfolio_metrics(
194
- selected_stocks, simulation_days, int(num_simulations)
195
- )
196
-
197
- if metrics is None:
198
- return self.create_error_html("Analysis failed. Please try different stocks."), None, None, None
199
-
200
- sim_plot = self.create_simulation_plot(simulations, selected_stocks)
201
- dist_plot = self.create_distribution_plot(simulations, metrics, selected_stocks)
202
- gauge_plot = self.create_risk_gauge(metrics)
203
- summary = self.create_summary_html(metrics, selected_stocks, simulation_days, num_simulations)
204
-
205
- return summary, sim_plot, dist_plot, gauge_plot
206
-
207
- except Exception as e:
208
- return self.create_error_html(f"Error: {str(e)}"), None, None, None
209
 
210
- def create_error_html(self, message):
211
- return f"""
212
- <div style='text-align: center; padding: 40px; background: #f8d7da; border-radius: 10px;'>
213
- <h3 style='color: #721c24;'>❌ Error</h3>
214
- <p>{message}</p>
215
- </div>
216
- """
217
-
218
- def create_summary_html(self, metrics, stocks, days, sims):
219
- risk_level = "LOW" if metrics['prob_10_loss'] < 0.1 else "MEDIUM" if metrics['prob_10_loss'] < 0.2 else "HIGH"
220
- risk_color = "#28a745" if risk_level == "LOW" else "#ffc107" if risk_level == "MEDIUM" else "#dc3545"
221
 
222
- # Create weights display
223
- weights_html = " β€’ ".join([f"{stock}: {weight}" for stock, weight in zip(stocks, metrics['weights'])])
224
 
225
- portfolio_type = "Single Stock" if len(stocks) == 1 else f"{len(stocks)}-Stock Portfolio"
226
 
227
  return f"""
228
- <div style="padding: 25px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 15px; color: white;">
229
- <h2 style="text-align: center;">πŸ“Š {portfolio_type} Risk Analysis</h2>
230
-
231
- <div style="background: rgba(255,255,255,0.1); padding: 20px; border-radius: 10px; margin: 15px 0;">
232
- <h3>Portfolio: {', '.join(stocks)}</h3>
233
- <p>Weights: {weights_html} | Period: {days} days | Simulations: {sims}</p>
234
  </div>
235
 
236
- <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px;">
237
- <div style="background: rgba(255,255,255,0.2); padding: 15px; border-radius: 8px;">
238
  <h4>Expected Value</h4>
239
- <p style="font-size: 24px; font-weight: bold;">${metrics['expected_value']:.2f}</p>
240
  </div>
241
-
242
- <div style="background: rgba(255,255,255,0.2); padding: 15px; border-radius: 8px;">
243
  <h4>Risk Level</h4>
244
- <p style="font-size: 24px; font-weight: bold; color: {risk_color};">{risk_level}</p>
245
  </div>
246
-
247
- <div style="background: rgba(255,255,255,0.2); padding: 15px; border-radius: 8px;">
248
- <h4>10% Loss Probability</h4>
249
- <p style="font-size: 24px; font-weight: bold;">{metrics['prob_10_loss']*100:.1f}%</p>
250
  </div>
251
-
252
- <div style="background: rgba(255,255,255,0.2); padding: 15px; border-radius: 8px;">
253
- <h4>Worst Case (5%)</h4>
254
- <p style="font-size: 24px; font-weight: bold;">${metrics['var_95']:.2f}</p>
255
  </div>
256
  </div>
257
-
258
- <div style="margin-top: 15px; background: rgba(255,255,255,0.1); padding: 15px; border-radius: 8px;">
259
- <h4>Additional Risk Metrics</h4>
260
- <p>20% Loss: <strong>{metrics['prob_20_loss']*100:.1f}%</strong> |
261
- 30% Loss: <strong>{metrics['prob_30_loss']*100:.1f}%</strong> |
262
- Best Case: <strong>${metrics['best_case']:.2f}</strong></p>
263
- </div>
264
  </div>
265
  """
266
 
267
- # Create and launch app
268
- app = SimpleRiskApp()
269
 
270
- with gr.Blocks(theme=gr.themes.Soft(), title="QuantRisk Pro") as demo:
271
- gr.Markdown("# πŸ“Š QuantRisk Pro - Portfolio Risk Analyzer")
272
- gr.Markdown("**Analyze single stocks or multi-stock portfolios with real-time risk assessment**")
 
273
 
274
  with gr.Row():
275
- with gr.Column(scale=1):
276
- gr.Markdown("### πŸ”§ Portfolio Configuration")
277
-
278
  stock_checkboxes = gr.CheckboxGroup(
279
- choices=app.available_stocks,
280
- value=['AAPL', 'MSFT', 'GOOGL'], # Default selection
281
- label="**Select Stocks (Choose 1 or more)**",
282
- info="Single stock or portfolio analysis"
283
  )
284
 
285
- simulation_days = gr.Slider(
286
- minimum=30,
287
- maximum=500,
288
- value=252,
289
- step=30,
290
- label="**Time Horizon (Days)**",
291
- info="252 days = 1 trading year"
292
- )
293
-
294
- num_simulations = gr.Dropdown(
295
- choices=[1000, 2500, 5000, 10000],
296
- value=5000,
297
- label="**Number of Simulations**"
298
- )
299
-
300
- analyze_btn = gr.Button("πŸš€ Run Risk Analysis", variant="primary", size="lg")
301
 
302
- gr.Markdown("---")
303
- gr.Markdown("""
304
- **πŸ’‘ How to Use:**
305
- - Select 1 stock for individual analysis
306
- - Select 2+ stocks for portfolio analysis
307
- - Weights automatically calculated as equal
308
- - Uses real market data from Yahoo Finance
309
- """)
310
 
311
- with gr.Column(scale=2):
312
- gr.Markdown("### πŸ“ˆ Analysis Results")
313
-
314
- summary_html = gr.HTML(
315
- value="""
316
- <div style='text-align: center; padding: 50px; background: #f8f9fa; border-radius: 10px;'>
317
- <h3 style='color: #6c757d;'>πŸš€ Ready to Analyze!</h3>
318
- <p>Select stocks and click 'Run Risk Analysis' to see your risk report.</p>
319
- </div>
320
- """
321
- )
322
 
323
  with gr.Row():
324
- risk_gauge = gr.Plot(label="**Risk Probability Gauge**")
325
- simulation_plot = gr.Plot(label="**Monte Carlo Simulation**")
326
 
327
- distribution_plot = gr.Plot(label="**Portfolio Value Distribution**")
328
 
329
- # Footer
330
- gr.Markdown("---")
331
- gr.Markdown(
332
- """
333
- <div style="text-align: center; color: #666;">
334
- <p>Built with ❀️ using Gradio | QuantRisk Pro | Real-time market data</p>
335
- </div>
336
- """
337
- )
 
 
338
 
339
- # Set up event handling
340
  analyze_btn.click(
341
- fn=app.analyze_portfolio,
342
- inputs=[stock_checkboxes, simulation_days, num_simulations],
343
- outputs=[summary_html, simulation_plot, distribution_plot, risk_gauge]
344
  )
345
 
346
  if __name__ == "__main__":
347
- demo.launch()
 
7
  import warnings
8
  warnings.filterwarnings('ignore')
9
 
10
+ class FreshRiskApp:
11
  def __init__(self):
12
+ # Hardcoded stock names to avoid encoding issues
13
+ self.stock_list = [
14
+ "AAPL - Apple",
15
+ "MSFT - Microsoft",
16
+ "GOOGL - Google",
17
+ "AMZN - Amazon",
18
+ "TSLA - Tesla",
19
+ "META - Meta",
20
+ "NVDA - NVIDIA",
21
+ "NFLX - Netflix",
22
+ "JPM - JPMorgan",
23
+ "JNJ - Johnson & Johnson"
24
  ]
25
+ self.stock_symbols = ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'TSLA', 'META', 'NVDA', 'NFLX', 'JPM', 'JNJ']
26
+ print("βœ… Fresh Risk Analyzer Ready!")
27
 
28
+ def get_stock_data(self, selected_indices, period='2y'):
29
+ """Get data for selected stock indices"""
30
  try:
31
+ symbols = [self.stock_symbols[i] for i in selected_indices]
32
+ print(f"πŸ“Š Fetching data for: {symbols}")
33
+
34
  data = yf.download(symbols, period=period, progress=False)['Adj Close']
35
+ if data.empty:
36
+ return None, None
37
+ return data, symbols
38
  except Exception as e:
39
  print(f"❌ Data fetch error: {str(e)}")
 
 
 
 
 
40
  return None, None
41
+
42
+ def run_analysis(self, selected_indices, days=252, simulations=5000):
43
+ """Run complete risk analysis"""
44
+ if not selected_indices:
45
+ return None, None, "Please select at least one stock"
46
 
47
  try:
48
+ # Get data
49
+ data, symbols = self.get_stock_data(selected_indices)
50
+ if data is None:
51
+ return None, None, "Failed to fetch stock data"
 
 
52
 
53
  # Calculate returns
54
  returns = data.pct_change().dropna()
55
+ if returns.empty:
56
+ return None, None, "Insufficient data for analysis"
57
 
58
+ n_stocks = len(symbols)
59
  weights = np.array([1/n_stocks] * n_stocks)
60
 
61
  # Calculate parameters
62
  mean_returns = returns.mean().values
63
  cov_matrix = returns.cov().values
64
 
65
+ # Make covariance matrix positive definite
66
  min_eig = np.min(np.real(np.linalg.eigvals(cov_matrix)))
67
  if min_eig < 0:
68
  cov_matrix -= 10 * min_eig * np.eye(*cov_matrix.shape)
69
 
70
+ print(f"🎯 Running {simulations} simulations for {symbols}...")
71
+
72
  # Generate simulation
73
  np.random.seed(42)
74
  L = np.linalg.cholesky(cov_matrix)
 
76
  simulation_results = []
77
 
78
  for i in range(simulations):
 
79
  random_numbers = np.random.normal(0, 1, size=(days, n_stocks))
80
  correlated_returns = random_numbers @ L.T + mean_returns
81
 
 
82
  portfolio_value = 100.0
83
  portfolio_path = [portfolio_value]
84
 
 
97
  'expected_value': float(np.mean(final_values)),
98
  'prob_10_loss': float(np.mean(final_values < 90)),
99
  'prob_20_loss': float(np.mean(final_values < 80)),
 
100
  'var_95': float(np.percentile(final_values, 5)),
101
  'best_case': float(np.percentile(final_values, 95)),
102
+ 'stocks': symbols,
103
  'weights': [f"{(1/n_stocks)*100:.1f}%" for _ in range(n_stocks)]
104
  }
105
 
106
+ print("βœ… Analysis completed successfully!")
107
+ return metrics, simulation_array, None
108
 
109
  except Exception as e:
110
  print(f"❌ Analysis error: {str(e)}")
111
+ return None, None, f"Analysis failed: {str(e)}"
112
 
113
+ def create_plots(self, metrics, simulations):
114
+ """Create all visualization plots"""
115
+ if metrics is None or simulations is None:
116
+ return None, None, None
117
+
118
+ # Simulation plot
119
+ sim_fig = go.Figure()
120
+ for i in range(min(20, len(simulations))):
121
+ sim_fig.add_trace(go.Scatter(
122
+ y=simulations[i], mode='lines', line=dict(width=1, color='blue'), opacity=0.1, showlegend=False
 
 
 
 
 
123
  ))
124
 
125
+ mean_path = np.mean(simulations, axis=0)
126
+ sim_fig.add_trace(go.Scatter(y=mean_path, mode='lines', line=dict(width=3, color='red'), name='Average'))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
 
128
+ stock_label = metrics['stocks'][0] if len(metrics['stocks']) == 1 else f"{len(metrics['stocks'])} Stocks"
129
+ sim_fig.update_layout(title=f"Monte Carlo Simulation - {stock_label}", height=400)
 
 
 
 
 
130
 
131
+ # Distribution plot
132
+ final_values = simulations[:, -1]
133
+ dist_fig = px.histogram(x=final_values, nbins=50, title="Portfolio Value Distribution")
134
+ dist_fig.add_vline(x=metrics['expected_value'], line_dash="dash", line_color="red")
135
+ dist_fig.add_vline(x=metrics['var_95'], line_dash="dash", line_color="orange")
136
+ dist_fig.update_layout(height=400)
 
 
 
 
 
 
137
 
138
+ # Risk gauge
139
  risk_prob = metrics['prob_10_loss'] * 100
140
+ gauge_fig = go.Figure(go.Indicator(
141
+ mode="gauge+number", value=risk_prob, title={'text': "10% Loss Probability"},
142
+ gauge={'axis': {'range': [0, 50]}, 'bar': {'color': "darkblue"},
143
+ 'steps': [{'range': [0, 15], 'color': "green"},
144
+ {'range': [15, 30], 'color': "yellow"},
145
+ {'range': [30, 50], 'color': "red"}]}
 
 
 
 
 
 
 
 
 
146
  ))
147
+ gauge_fig.update_layout(height=300)
 
 
 
 
 
 
 
 
148
 
149
+ return sim_fig, dist_fig, gauge_fig
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
+ def create_summary(self, metrics, days, sims):
152
+ """Create summary HTML"""
153
+ if metrics is None:
154
+ return "No results available"
 
 
 
 
 
 
 
155
 
156
+ risk_level = "LOW" if metrics['prob_10_loss'] < 0.1 else "MEDIUM" if metrics['prob_10_loss'] < 0.2 else "HIGH"
157
+ risk_color = "green" if risk_level == "LOW" else "orange" if risk_level == "MEDIUM" else "red"
158
 
159
+ weights_text = " β€’ ".join([f"{stock}: {weight}" for stock, weight in zip(metrics['stocks'], metrics['weights'])])
160
 
161
  return f"""
162
+ <div style="padding: 20px; background: #f0f8ff; border-radius: 10px;">
163
+ <h2>πŸ“Š Risk Analysis Report</h2>
164
+ <div style="background: white; padding: 15px; border-radius: 8px; margin: 10px 0;">
165
+ <h3>Portfolio: {', '.join(metrics['stocks'])}</h3>
166
+ <p><strong>Weights:</strong> {weights_text}</p>
167
+ <p><strong>Period:</strong> {days} days | <strong>Simulations:</strong> {sims}</p>
168
  </div>
169
 
170
+ <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
171
+ <div style="background: white; padding: 10px; border-radius: 5px;">
172
  <h4>Expected Value</h4>
173
+ <p style="font-size: 20px; color: blue;">${metrics['expected_value']:.2f}</p>
174
  </div>
175
+ <div style="background: white; padding: 10px; border-radius: 5px;">
 
176
  <h4>Risk Level</h4>
177
+ <p style="font-size: 20px; color: {risk_color};">{risk_level}</p>
178
  </div>
179
+ <div style="background: white; padding: 10px; border-radius: 5px;">
180
+ <h4>10% Loss Chance</h4>
181
+ <p style="font-size: 20px; color: orange;">{metrics['prob_10_loss']*100:.1f}%</p>
 
182
  </div>
183
+ <div style="background: white; padding: 10px; border-radius: 5px;">
184
+ <h4>Worst Case</h4>
185
+ <p style="font-size: 20px; color: red;">${metrics['var_95']:.2f}</p>
 
186
  </div>
187
  </div>
 
 
 
 
 
 
 
188
  </div>
189
  """
190
 
191
+ # Create app instance
192
+ app = FreshRiskApp()
193
 
194
+ # Build Gradio interface
195
+ with gr.Blocks(title="Stock Risk Analyzer") as demo:
196
+ gr.Markdown("# πŸ“ˆ Stock Portfolio Risk Analyzer")
197
+ gr.Markdown("**Analyze risk for single stocks or portfolios using Monte Carlo simulations**")
198
 
199
  with gr.Row():
200
+ with gr.Column():
201
+ gr.Markdown("### Select Stocks")
 
202
  stock_checkboxes = gr.CheckboxGroup(
203
+ choices=app.stock_list,
204
+ label="Choose stocks for analysis",
205
+ value=[0, 1, 2] # Default: AAPL, MSFT, GOOGL
 
206
  )
207
 
208
+ gr.Markdown("### Simulation Settings")
209
+ days_slider = gr.Slider(30, 500, 252, label="Time Horizon (Days)")
210
+ sims_dropdown = gr.Dropdown([1000, 5000, 10000], value=5000, label="Number of Simulations")
 
 
 
 
 
 
 
 
 
 
 
 
 
211
 
212
+ analyze_btn = gr.Button("πŸš€ Run Analysis", variant="primary")
 
 
 
 
 
 
 
213
 
214
+ with gr.Column():
215
+ gr.Markdown("### Analysis Results")
216
+ results_html = gr.HTML()
 
 
 
 
 
 
 
 
217
 
218
  with gr.Row():
219
+ gauge_plot = gr.Plot()
220
+ sim_plot = gr.Plot()
221
 
222
+ dist_plot = gr.Plot()
223
 
224
+ # Handle analysis
225
+ def run_complete_analysis(stock_indices, days, sims):
226
+ metrics, simulations, error = app.run_analysis(stock_indices, days, sims)
227
+
228
+ if error:
229
+ return f"<div style='color: red; padding: 20px;'><h3>❌ Error</h3><p>{error}</p></div>", None, None, None
230
+
231
+ summary = app.create_summary(metrics, days, sims)
232
+ sim_fig, dist_fig, gauge_fig = app.create_plots(metrics, simulations)
233
+
234
+ return summary, gauge_fig, sim_fig, dist_fig
235
 
 
236
  analyze_btn.click(
237
+ fn=run_complete_analysis,
238
+ inputs=[stock_checkboxes, days_slider, sims_dropdown],
239
+ outputs=[results_html, gauge_plot, sim_plot, dist_plot]
240
  )
241
 
242
  if __name__ == "__main__":
243
+ demo.launch(share=True)