Darshan03 commited on
Commit
924e27e
·
verified ·
1 Parent(s): 1aef627

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +121 -97
app.py CHANGED
@@ -13,17 +13,18 @@ import pickle
13
 
14
  import requests
15
  from bs4 import BeautifulSoup
16
- import nest_asyncio
17
- import asyncio
18
  import re
19
- # from crawl4ai import * # removed
20
  import google.generativeai as genai
21
  import numpy as np
22
- import matplotlib.pyplot as plt
 
 
23
 
24
  # Load environment variables
25
  load_dotenv()
26
 
 
 
27
 
28
  # Configuration
29
  class Config:
@@ -61,15 +62,17 @@ def fetch_stock_data(symbols: List[str]) -> Dict[str, pd.DataFrame | None]:
61
  stock_dataframes = {}
62
  for symbol in symbols:
63
  try:
 
64
  ticker = yf.Ticker(symbol)
65
  data = ticker.history(period="max")
66
  if data.empty:
67
- print(f"Warning: No data found for symbol '{symbol}'.")
68
  stock_dataframes[symbol] = None
69
  continue
70
  stock_dataframes[symbol] = data
 
71
  except Exception as e:
72
- print(f"Error fetching data for symbol '{symbol}': {e}")
73
  stock_dataframes[symbol] = None
74
  return stock_dataframes
75
 
@@ -82,12 +85,13 @@ def store_stock_data(stock_dataframes: Dict[str, pd.DataFrame | None],
82
  file_name = f"{symbol}_daily_data.csv"
83
  file_path = os.path.join(output_path, file_name)
84
  try:
 
85
  data.to_csv(file_path)
86
- print(f"Info: Data for '{symbol}' saved to {file_path}")
87
  except Exception as e:
88
- print(f"Error saving data for '{symbol}' to {file_path}: {e}")
89
  else:
90
- print(f"Warning: No data available for '{symbol}', skipping storage.")
91
 
92
 
93
  def load_stock_data_and_extract_price(output_path_dir: str) -> Dict[str, Dict[str, float]]:
@@ -98,15 +102,17 @@ def load_stock_data_and_extract_price(output_path_dir: str) -> Dict[str, Dict[st
98
  symbol = filename.replace("_daily_data.csv", "")
99
  file_path = os.path.join(output_path_dir, filename)
100
  try:
 
101
  df = pd.read_csv(file_path, index_col=0)
102
  if not df.empty:
103
  initial_price = df.iloc[-1]['Close']
104
  all_stock_data[symbol] = {"initial_price": initial_price}
 
105
  else:
106
- print(f"Warning: Empty dataframe for symbol '{symbol}'. Setting initial price to 0")
107
  all_stock_data[symbol] = {"initial_price": 0.0}
108
  except (IndexError, KeyError, FileNotFoundError) as e:
109
- print(f"Error occurred for reading {symbol}, due to: {e}")
110
  all_stock_data[symbol] = {"initial_price": 0.0} # default initial price is 0.0
111
 
112
  return all_stock_data
@@ -119,8 +125,10 @@ def merge_stock_data_with_price(stock_data: Dict, extracted_data: Dict) -> Dict:
119
  symbol = value["symbol"]
120
  if symbol in extracted_data:
121
  merged_stock_data[key]["initial_price"] = extracted_data[symbol]["initial_price"]
 
122
  else:
123
  merged_stock_data[key]["initial_price"] = 0.0 # default value if it cannot be extracted
 
124
  return merged_stock_data
125
 
126
 
@@ -137,6 +145,7 @@ def generate_prompt(stock_data: Dict) -> str:
137
  """
138
  stock_json_str = json.dumps(stock_data)
139
  formatted_prompt_with_price = prompt_template_with_price.format(stock_data=stock_json_str)
 
140
  return formatted_prompt_with_price
141
 
142
 
@@ -167,41 +176,45 @@ def invoke_llm_for_portfolio(formatted_prompt: str) -> Portfolio:
167
  llm = ChatGroq(groq_api_key=Config.GROQ_API_KEY, model_name="llama-3.1-8b-instant")
168
  structured_llm = llm.with_structured_output(Portfolio)
169
  try:
 
170
  output = structured_llm.invoke(formatted_prompt)
 
171
  return output
172
  except ValidationError as e:
173
- print(f"Error during LLM invocation: {e}")
174
  raise
175
  except Exception as e:
176
- print(f"Unexpected error during LLM invocation {e}")
177
  raise
178
 
179
 
180
  def portfolio_to_json(portfolio: Portfolio, output_file: str = Config.OUTPUT_FILE) -> None:
181
  """Converts a Portfolio object to a JSON string and saves it to a file."""
182
  try:
 
183
  json_str = portfolio.model_dump_json(indent=4)
184
  with open(output_file, "w") as f:
185
  f.write(json_str)
186
- print(f"Info: Portfolio saved to '{output_file}'")
187
  except Exception as e:
188
- print(f"Error saving JSON file {e}")
189
 
190
 
191
  # --------------------- Function from scenario.py ---------------------
192
- # Removed nest_asyncio.apply()
193
  def scrape_website(url):
194
  headers = {
195
  "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
196
  }
197
-
198
- response = requests.get(url, headers=headers)
199
-
200
- if response.status_code == 200:
201
  soup = BeautifulSoup(response.text, "html.parser")
202
- return soup.prettify() # Returns the full parsed HTML
203
- else:
204
- return f"Failed to retrieve page. Status code: {response.status_code}"
 
 
205
 
206
 
207
  genai.configure(api_key=Config.GOOGLE_API_KEY) # Replace with your API key
@@ -222,15 +235,19 @@ chat_session = model.start_chat()
222
 
223
 
224
  def get_response(llm, prompt):
 
225
  response = llm.send_message(prompt)
 
226
  return response
227
 
228
 
229
  def extract_json_content(text):
230
  match = re.search(r"```json\n(.*?)```", text, re.DOTALL)
231
  if match:
 
232
  return match.group(1).strip()
233
  else:
 
234
  return None
235
 
236
 
@@ -285,13 +302,14 @@ def invoke_llm_for_scenario(context_data):
285
  os.makedirs(os.path.dirname(output_file), exist_ok=True)
286
  with open(output_file, "w") as f:
287
  json.dump(analysis_json, f, indent=4)
288
- print(f"Analysis saved to '{output_file}'")
289
  return analysis_json
290
  except json.JSONDecodeError:
291
- print("Error: Could not decode the output from the model into JSON format.")
 
292
  except Exception as e:
293
- print(f"Error: {e}")
294
- return None
295
 
296
 
297
  # --------------------- Function from simulation_data.py ---------------------
@@ -343,18 +361,19 @@ def monte_carlo_simulation(portfolio_data, scenario_data, num_simulations=10000)
343
  75: np.percentile(returns, 75),
344
  95: np.percentile(returns, 95),
345
  }
346
-
347
  return results
348
 
349
 
350
  def load_dataframes(filename="output_files/saved_dataframes.pkl"):
351
  try:
 
352
  with open(filename, 'rb') as file:
353
  saved_dataframes = pickle.load(file)
354
- print(f"DataFrames successfully loaded from {filename}.")
355
  return saved_dataframes
356
  except FileNotFoundError:
357
- print(f"File {filename} not found.")
358
  return None
359
 
360
 
@@ -393,7 +412,7 @@ def calculate_scenario_magnitudes(portfolio_data, scenario_data, saved_dataframe
393
  "individual_magnitudes": sector_magnitudes,
394
  "aggregated_magnitude": aggregated_magnitude,
395
  }
396
-
397
  return scenario_results
398
 
399
 
@@ -404,6 +423,7 @@ def update_scenario_data(scenario_data, scenario_results):
404
  # Update aggregated magnitude
405
  scenario_data["market_scenarios"][scenario_id]["aggregated_magnitude"] = results["aggregated_magnitude"]
406
 
 
407
  return scenario_data
408
 
409
 
@@ -432,58 +452,60 @@ def main():
432
  uploaded_file = st.file_uploader("Upload your stock_data.json file", type=["json"])
433
 
434
  if uploaded_file:
435
- try:
436
- stock_data = json.load(uploaded_file)
437
- st.session_state['uploaded_stock_data'] = stock_data
438
- st.success("Stock data file uploaded successfully!")
439
-
440
- # Fetch stock data
441
- stock_symbols = [value["symbol"] for value in stock_data.values()]
442
- stock_dfs = fetch_stock_data(stock_symbols)
443
-
444
- # Save DataFrames in a dictionary for future use
445
- saved_dataframes = {}
446
- if stock_dfs:
 
447
  for symbol, df in stock_dfs.items():
448
  if df is not None:
449
  # Save DataFrame in the variable
450
  saved_dataframes[symbol] = df
451
- print(f"Data for '{symbol}' loaded into variable.")
452
  else:
453
- print(f"No data found for '{symbol}'")
454
- else:
455
- print("Error occurred during fetching data. DataFrames are not returned.")
456
 
457
- # Save the dictionary to a local file
458
- with open('output_files/saved_dataframes.pkl', 'wb') as file:
459
  pickle.dump(saved_dataframes, file)
460
- print(f"DataFrames successfully saved to output_files/saved_dataframes.pkl.")
461
- st.session_state['saved_dataframes'] = saved_dataframes
462
- # Store Data
463
- store_stock_data(stock_dfs)
464
 
465
- # Load the last price
466
- extracted_data = load_stock_data_and_extract_price(Config.STOCK_DATA_DIR)
467
 
468
- # Merge extracted price with the main dictionary
469
- merged_stock_data = merge_stock_data_with_price(stock_data, extracted_data)
470
- st.session_state['stock_data'] = merged_stock_data
471
 
472
- # Generate prompt for LLM
473
- formatted_prompt = generate_prompt(merged_stock_data)
474
 
475
- # Invoke LLM
476
- try:
477
  portfolio_output = invoke_llm_for_portfolio(formatted_prompt)
478
  portfolio_to_json(portfolio_output)
479
  st.session_state['portfolio_data'] = portfolio_output.model_dump()
480
  st.success("Stock data processed successfully. Portfolio data generated!")
481
- except Exception as e:
482
  st.error(f"An unexpected error occurred during the LLM invocation: {e}")
483
- except json.JSONDecodeError:
484
- st.error("Invalid JSON format. Please upload a valid JSON file.")
485
- except Exception as e:
486
- st.error(f"An error occurred while processing the uploaded file: {e}")
 
487
 
488
  st.header("2. Fetch Market Scenario")
489
  # Input for market analysis URL
@@ -492,41 +514,43 @@ def main():
492
  fetch_market_scenario = st.button("Fetch Market Scenario")
493
 
494
  if fetch_market_scenario:
495
- # Market Analysis
496
- context_data = scrape_website(url) # Changed here
497
- scenario_data = invoke_llm_for_scenario(context_data)
498
- if scenario_data:
499
- st.session_state['scenario_data'] = scenario_data
500
- st.success("Market scenario data generated")
501
- else:
502
- st.error("Error occurred while generating market scenarios")
 
503
 
504
  st.header("3. Run Simulation")
505
 
506
  run_simulation = st.button("Run Monte Carlo Simulation")
507
  if run_simulation:
508
- if st.session_state['portfolio_data'] and st.session_state['scenario_data']:
509
- saved_dataframes = st.session_state['saved_dataframes']
510
- # Update scenario data with magnitudes
511
- scenario_results = calculate_scenario_magnitudes(st.session_state['portfolio_data'],
512
- st.session_state['scenario_data'], saved_dataframes)
513
- updated_scenario_data = update_scenario_data(st.session_state['scenario_data'], scenario_results)
514
- # Run Monte Carlo simulation
515
- simulation_results = monte_carlo_simulation(st.session_state['portfolio_data'], updated_scenario_data)
516
- st.session_state['simulation_results'] = simulation_results
517
- # Display simulation results
518
- st.subheader("Simulation Results")
519
- for scenario_name, results in simulation_results.items():
520
- st.write(f"**Scenario:** {scenario_name}")
521
- st.write(f" **Average Return:** {results['average_return']:.4f}")
522
- st.write(f" **Std Dev Return:** {results['std_dev_return']:.4f}")
523
- st.write(" **Return Percentiles:**")
524
- for percentile, value in results["percentiles"].items():
525
- st.write(f" {percentile}th: {value:.4f}")
526
- st.write("-" * 40)
527
- st.success("Monte Carlo simulation completed.")
528
- else:
529
- st.error("Please ensure both portfolio and scenario data are available.")
 
530
 
531
 
532
  if __name__ == "__main__":
 
13
 
14
  import requests
15
  from bs4 import BeautifulSoup
 
 
16
  import re
 
17
  import google.generativeai as genai
18
  import numpy as np
19
+ import logging # Added logging
20
+ import time # added time for loading animation
21
+
22
 
23
  # Load environment variables
24
  load_dotenv()
25
 
26
+ # Configure logging
27
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
28
 
29
  # Configuration
30
  class Config:
 
62
  stock_dataframes = {}
63
  for symbol in symbols:
64
  try:
65
+ logging.info(f"Fetching stock data for {symbol}")
66
  ticker = yf.Ticker(symbol)
67
  data = ticker.history(period="max")
68
  if data.empty:
69
+ logging.warning(f"No data found for symbol '{symbol}'.")
70
  stock_dataframes[symbol] = None
71
  continue
72
  stock_dataframes[symbol] = data
73
+ logging.info(f"Successfully fetched stock data for {symbol}")
74
  except Exception as e:
75
+ logging.error(f"Error fetching data for symbol '{symbol}': {e}")
76
  stock_dataframes[symbol] = None
77
  return stock_dataframes
78
 
 
85
  file_name = f"{symbol}_daily_data.csv"
86
  file_path = os.path.join(output_path, file_name)
87
  try:
88
+ logging.info(f"Saving data for '{symbol}' to {file_path}")
89
  data.to_csv(file_path)
90
+ logging.info(f"Data for '{symbol}' saved to {file_path}")
91
  except Exception as e:
92
+ logging.error(f"Error saving data for '{symbol}' to {file_path}: {e}")
93
  else:
94
+ logging.warning(f"No data available for '{symbol}', skipping storage.")
95
 
96
 
97
  def load_stock_data_and_extract_price(output_path_dir: str) -> Dict[str, Dict[str, float]]:
 
102
  symbol = filename.replace("_daily_data.csv", "")
103
  file_path = os.path.join(output_path_dir, filename)
104
  try:
105
+ logging.info(f"Loading data from {file_path} for symbol {symbol}")
106
  df = pd.read_csv(file_path, index_col=0)
107
  if not df.empty:
108
  initial_price = df.iloc[-1]['Close']
109
  all_stock_data[symbol] = {"initial_price": initial_price}
110
+ logging.info(f"Initial price extracted for {symbol}: {initial_price}")
111
  else:
112
+ logging.warning(f"Empty dataframe for symbol '{symbol}'. Setting initial price to 0")
113
  all_stock_data[symbol] = {"initial_price": 0.0}
114
  except (IndexError, KeyError, FileNotFoundError) as e:
115
+ logging.error(f"Error occurred for reading {symbol}, due to: {e}")
116
  all_stock_data[symbol] = {"initial_price": 0.0} # default initial price is 0.0
117
 
118
  return all_stock_data
 
125
  symbol = value["symbol"]
126
  if symbol in extracted_data:
127
  merged_stock_data[key]["initial_price"] = extracted_data[symbol]["initial_price"]
128
+ logging.info(f"Merged initial price for {symbol} in main stock data")
129
  else:
130
  merged_stock_data[key]["initial_price"] = 0.0 # default value if it cannot be extracted
131
+ logging.warning(f"Could not extract price for {symbol}. Setting default value to 0")
132
  return merged_stock_data
133
 
134
 
 
145
  """
146
  stock_json_str = json.dumps(stock_data)
147
  formatted_prompt_with_price = prompt_template_with_price.format(stock_data=stock_json_str)
148
+ logging.info(f"Generated LLM prompt: {formatted_prompt_with_price}")
149
  return formatted_prompt_with_price
150
 
151
 
 
176
  llm = ChatGroq(groq_api_key=Config.GROQ_API_KEY, model_name="llama-3.1-8b-instant")
177
  structured_llm = llm.with_structured_output(Portfolio)
178
  try:
179
+ logging.info(f"Invoking LLM for portfolio generation")
180
  output = structured_llm.invoke(formatted_prompt)
181
+ logging.info(f"LLM returned Portfolio data {output}")
182
  return output
183
  except ValidationError as e:
184
+ logging.error(f"Error during LLM invocation: {e}")
185
  raise
186
  except Exception as e:
187
+ logging.error(f"Unexpected error during LLM invocation {e}")
188
  raise
189
 
190
 
191
  def portfolio_to_json(portfolio: Portfolio, output_file: str = Config.OUTPUT_FILE) -> None:
192
  """Converts a Portfolio object to a JSON string and saves it to a file."""
193
  try:
194
+ logging.info(f"Saving portfolio to JSON file: {output_file}")
195
  json_str = portfolio.model_dump_json(indent=4)
196
  with open(output_file, "w") as f:
197
  f.write(json_str)
198
+ logging.info(f"Portfolio saved to '{output_file}'")
199
  except Exception as e:
200
+ logging.error(f"Error saving JSON file {e}")
201
 
202
 
203
  # --------------------- Function from scenario.py ---------------------
 
204
  def scrape_website(url):
205
  headers = {
206
  "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
207
  }
208
+ logging.info(f"Scraping website: {url}")
209
+ try:
210
+ response = requests.get(url, headers=headers)
211
+ response.raise_for_status() # Raise an exception for bad status codes
212
  soup = BeautifulSoup(response.text, "html.parser")
213
+ logging.info(f"Successfully scraped website: {url}")
214
+ return soup.prettify()
215
+ except requests.exceptions.RequestException as e:
216
+ logging.error(f"Failed to retrieve page. Status code: {e}")
217
+ return f"Failed to retrieve page. Status code: {e}"
218
 
219
 
220
  genai.configure(api_key=Config.GOOGLE_API_KEY) # Replace with your API key
 
235
 
236
 
237
  def get_response(llm, prompt):
238
+ logging.info(f"Sending prompt to LLM for scenario: {prompt}")
239
  response = llm.send_message(prompt)
240
+ logging.info(f"LLM returned a response for scenario")
241
  return response
242
 
243
 
244
  def extract_json_content(text):
245
  match = re.search(r"```json\n(.*?)```", text, re.DOTALL)
246
  if match:
247
+ logging.info("Extracted JSON content from LLM response")
248
  return match.group(1).strip()
249
  else:
250
+ logging.warning("Could not extract JSON content from LLM response")
251
  return None
252
 
253
 
 
302
  os.makedirs(os.path.dirname(output_file), exist_ok=True)
303
  with open(output_file, "w") as f:
304
  json.dump(analysis_json, f, indent=4)
305
+ logging.info(f"Analysis saved to '{output_file}'")
306
  return analysis_json
307
  except json.JSONDecodeError:
308
+ logging.error("Could not decode the output from the model into JSON format.")
309
+ return None
310
  except Exception as e:
311
+ logging.error(f"Error: {e}")
312
+ return None
313
 
314
 
315
  # --------------------- Function from simulation_data.py ---------------------
 
361
  75: np.percentile(returns, 75),
362
  95: np.percentile(returns, 95),
363
  }
364
+ logging.info(f"Monte Carlo simulation completed for scenario {scenario_name}")
365
  return results
366
 
367
 
368
  def load_dataframes(filename="output_files/saved_dataframes.pkl"):
369
  try:
370
+ logging.info(f"Loading dataframes from file: {filename}")
371
  with open(filename, 'rb') as file:
372
  saved_dataframes = pickle.load(file)
373
+ logging.info(f"DataFrames successfully loaded from {filename}.")
374
  return saved_dataframes
375
  except FileNotFoundError:
376
+ logging.error(f"File {filename} not found.")
377
  return None
378
 
379
 
 
412
  "individual_magnitudes": sector_magnitudes,
413
  "aggregated_magnitude": aggregated_magnitude,
414
  }
415
+ logging.info(f"Magnitudes calculated for scenario {scenario_name}")
416
  return scenario_results
417
 
418
 
 
423
  # Update aggregated magnitude
424
  scenario_data["market_scenarios"][scenario_id]["aggregated_magnitude"] = results["aggregated_magnitude"]
425
 
426
+ logging.info(f"Scenario data updated with calculated magnitudes")
427
  return scenario_data
428
 
429
 
 
452
  uploaded_file = st.file_uploader("Upload your stock_data.json file", type=["json"])
453
 
454
  if uploaded_file:
455
+ with st.spinner("Processing stock data..."):
456
+ try:
457
+ stock_data = json.load(uploaded_file)
458
+ st.session_state['uploaded_stock_data'] = stock_data
459
+ st.success("Stock data file uploaded successfully!")
460
+
461
+ # Fetch stock data
462
+ stock_symbols = [value["symbol"] for value in stock_data.values()]
463
+ stock_dfs = fetch_stock_data(stock_symbols)
464
+
465
+ # Save DataFrames in a dictionary for future use
466
+ saved_dataframes = {}
467
+ if stock_dfs:
468
  for symbol, df in stock_dfs.items():
469
  if df is not None:
470
  # Save DataFrame in the variable
471
  saved_dataframes[symbol] = df
472
+ logging.info(f"Data for '{symbol}' loaded into variable.")
473
  else:
474
+ logging.warning(f"No data found for '{symbol}'")
475
+ else:
476
+ logging.error("Error occurred during fetching data. DataFrames are not returned.")
477
 
478
+ # Save the dictionary to a local file
479
+ with open('output_files/saved_dataframes.pkl', 'wb') as file:
480
  pickle.dump(saved_dataframes, file)
481
+ logging.info(f"DataFrames successfully saved to output_files/saved_dataframes.pkl.")
482
+ st.session_state['saved_dataframes'] = saved_dataframes
483
+ # Store Data
484
+ store_stock_data(stock_dfs)
485
 
486
+ # Load the last price
487
+ extracted_data = load_stock_data_and_extract_price(Config.STOCK_DATA_DIR)
488
 
489
+ # Merge extracted price with the main dictionary
490
+ merged_stock_data = merge_stock_data_with_price(stock_data, extracted_data)
491
+ st.session_state['stock_data'] = merged_stock_data
492
 
493
+ # Generate prompt for LLM
494
+ formatted_prompt = generate_prompt(merged_stock_data)
495
 
496
+ # Invoke LLM
497
+ try:
498
  portfolio_output = invoke_llm_for_portfolio(formatted_prompt)
499
  portfolio_to_json(portfolio_output)
500
  st.session_state['portfolio_data'] = portfolio_output.model_dump()
501
  st.success("Stock data processed successfully. Portfolio data generated!")
502
+ except Exception as e:
503
  st.error(f"An unexpected error occurred during the LLM invocation: {e}")
504
+ except json.JSONDecodeError:
505
+ st.error("Invalid JSON format. Please upload a valid JSON file.")
506
+ except Exception as e:
507
+ st.error(f"An error occurred while processing the uploaded file: {e}")
508
+
509
 
510
  st.header("2. Fetch Market Scenario")
511
  # Input for market analysis URL
 
514
  fetch_market_scenario = st.button("Fetch Market Scenario")
515
 
516
  if fetch_market_scenario:
517
+ with st.spinner("Fetching market scenario..."):
518
+ # Market Analysis
519
+ context_data = scrape_website(url) # Changed here
520
+ scenario_data = invoke_llm_for_scenario(context_data)
521
+ if scenario_data:
522
+ st.session_state['scenario_data'] = scenario_data
523
+ st.success("Market scenario data generated")
524
+ else:
525
+ st.error("Error occurred while generating market scenarios")
526
 
527
  st.header("3. Run Simulation")
528
 
529
  run_simulation = st.button("Run Monte Carlo Simulation")
530
  if run_simulation:
531
+ with st.spinner("Running Monte Carlo Simulation..."):
532
+ if st.session_state['portfolio_data'] and st.session_state['scenario_data']:
533
+ saved_dataframes = st.session_state['saved_dataframes']
534
+ # Update scenario data with magnitudes
535
+ scenario_results = calculate_scenario_magnitudes(st.session_state['portfolio_data'],
536
+ st.session_state['scenario_data'], saved_dataframes)
537
+ updated_scenario_data = update_scenario_data(st.session_state['scenario_data'], scenario_results)
538
+ # Run Monte Carlo simulation
539
+ simulation_results = monte_carlo_simulation(st.session_state['portfolio_data'], updated_scenario_data)
540
+ st.session_state['simulation_results'] = simulation_results
541
+ # Display simulation results
542
+ st.subheader("Simulation Results")
543
+ for scenario_name, results in simulation_results.items():
544
+ st.write(f"**Scenario:** {scenario_name}")
545
+ st.write(f" **Average Return:** {results['average_return']:.4f}")
546
+ st.write(f" **Std Dev Return:** {results['std_dev_return']:.4f}")
547
+ st.write(" **Return Percentiles:**")
548
+ for percentile, value in results["percentiles"].items():
549
+ st.write(f" {percentile}th: {value:.4f}")
550
+ st.write("-" * 40)
551
+ st.success("Monte Carlo simulation completed.")
552
+ else:
553
+ st.error("Please ensure both portfolio and scenario data are available.")
554
 
555
 
556
  if __name__ == "__main__":