Update app.py
Browse files
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
|
|
|
|
|
|
|
| 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 |
-
|
| 68 |
stock_dataframes[symbol] = None
|
| 69 |
continue
|
| 70 |
stock_dataframes[symbol] = data
|
|
|
|
| 71 |
except Exception as e:
|
| 72 |
-
|
| 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 |
-
|
| 87 |
except Exception as e:
|
| 88 |
-
|
| 89 |
else:
|
| 90 |
-
|
| 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 |
-
|
| 107 |
all_stock_data[symbol] = {"initial_price": 0.0}
|
| 108 |
except (IndexError, KeyError, FileNotFoundError) as e:
|
| 109 |
-
|
| 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 |
-
|
| 174 |
raise
|
| 175 |
except Exception as e:
|
| 176 |
-
|
| 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 |
-
|
| 187 |
except Exception as e:
|
| 188 |
-
|
| 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 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
soup = BeautifulSoup(response.text, "html.parser")
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
|
|
|
|
|
|
| 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 |
-
|
| 289 |
return analysis_json
|
| 290 |
except json.JSONDecodeError:
|
| 291 |
-
|
|
|
|
| 292 |
except Exception as e:
|
| 293 |
-
|
| 294 |
-
|
| 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 |
-
|
| 355 |
return saved_dataframes
|
| 356 |
except FileNotFoundError:
|
| 357 |
-
|
| 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 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
|
|
|
| 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 |
-
|
| 452 |
else:
|
| 453 |
-
|
| 454 |
-
|
| 455 |
-
|
| 456 |
|
| 457 |
-
|
| 458 |
-
|
| 459 |
pickle.dump(saved_dataframes, file)
|
| 460 |
-
|
| 461 |
-
|
| 462 |
-
|
| 463 |
-
|
| 464 |
|
| 465 |
-
|
| 466 |
-
|
| 467 |
|
| 468 |
-
|
| 469 |
-
|
| 470 |
-
|
| 471 |
|
| 472 |
-
|
| 473 |
-
|
| 474 |
|
| 475 |
-
|
| 476 |
-
|
| 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 |
-
|
| 482 |
st.error(f"An unexpected error occurred during the LLM invocation: {e}")
|
| 483 |
-
|
| 484 |
-
|
| 485 |
-
|
| 486 |
-
|
|
|
|
| 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 |
-
|
| 496 |
-
|
| 497 |
-
|
| 498 |
-
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
|
|
|
|
| 503 |
|
| 504 |
st.header("3. Run Simulation")
|
| 505 |
|
| 506 |
run_simulation = st.button("Run Monte Carlo Simulation")
|
| 507 |
if run_simulation:
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
|
| 511 |
-
|
| 512 |
-
|
| 513 |
-
|
| 514 |
-
|
| 515 |
-
|
| 516 |
-
|
| 517 |
-
|
| 518 |
-
|
| 519 |
-
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
|
| 524 |
-
|
| 525 |
-
|
| 526 |
-
|
| 527 |
-
|
| 528 |
-
|
| 529 |
-
|
|
|
|
| 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__":
|