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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +461 -149
app.py CHANGED
@@ -1,35 +1,195 @@
 
 
1
  import os
2
  import json
3
- import pickle
4
- import time
5
- import streamlit as st
 
 
 
6
  from dotenv import load_dotenv
7
- from portfolio import (
8
- fetch_stock_data,
9
- store_stock_data,
10
- load_stock_data_and_extract_price,
11
- portfolio_to_json,
12
- merge_stock_data_with_price,
13
- generate_prompt,
14
- invoke_llm_for_portfolio
15
- )
16
- from scenario import (
17
- extract_text_from_website,
18
- get_response,
19
- extract_json_content
20
- )
21
  import asyncio
 
 
22
  import google.generativeai as genai
23
- from simluation_data import monte_carlo_simulation
 
24
 
25
- import os
26
- import sys
27
- import subprocess
28
 
29
- import requests
30
- from bs4 import BeautifulSoup
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  def scrape_website(url):
34
  headers = {
35
  "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"
@@ -44,113 +204,40 @@ def scrape_website(url):
44
  return f"Failed to retrieve page. Status code: {response.status_code}"
45
 
46
 
47
- # Load environment variables from .env
48
- load_dotenv()
49
-
50
- # Streamlit UI
51
- st.title("Stock Portfolio Analysis")
52
-
53
- uploaded_file = st.file_uploader("Upload JSON file", type=["json"])
54
-
55
- if uploaded_file is not None:
56
- with st.spinner("Loading data..."):
57
- time.sleep(2) # Simulating loading time
58
- stock_data = json.load(uploaded_file)
59
- st.success("Data loaded successfully!")
60
-
61
-
62
- # Configuration Class
63
- class Config:
64
- ALPHA_VANTAGE_API_KEY = os.getenv("ALPHA_VANTAGE_API_KEY")
65
- GROQ_API_KEY = os.getenv("GROQ_API_KEY")
66
- GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
67
- STOCK_DATA_DIR = "stock_data_NSE"
68
- OUTPUT_FILE = "output_files/portfolio.json"
69
- SCENARIO_OUTPUT_FILE = "output_files/scenario.json"
70
- SECTORS = [
71
- "Communication Services",
72
- "Consumer Discretionary",
73
- "Consumer Staples",
74
- "Energy",
75
- "Financials",
76
- "Health Care",
77
- "Industrials",
78
- "Information Technology",
79
- "Materials",
80
- "Real Estate",
81
- "Utilities"
82
- ]
83
-
84
-
85
- # Create directories if they don't exist
86
- os.makedirs(Config.STOCK_DATA_DIR, exist_ok=True)
87
- os.makedirs(os.path.dirname(Config.OUTPUT_FILE), exist_ok=True)
88
- os.makedirs(os.path.dirname(Config.SCENARIO_OUTPUT_FILE), exist_ok=True)
89
-
90
-
91
- def configure_generative_ai():
92
- """Configures the generative AI model and starts a chat session."""
93
- genai.configure(api_key=Config.GOOGLE_API_KEY)
94
 
95
- generation_config = {
96
- "temperature": 1,
97
- "top_p": 0.95,
98
- "top_k": 40,
99
- "max_output_tokens": 8192,
100
- "response_mime_type": "text/plain",
101
- }
102
-
103
- model = genai.GenerativeModel(
104
- model_name="gemini-2.0-flash-exp",
105
- generation_config=generation_config,
106
- )
107
-
108
- return model.start_chat()
109
-
110
-
111
- # Fetch stock data
112
- st.write("Fetching stock data...")
113
- stock_symbols = [value["symbol"] for value in stock_data.values()]
114
- stock_dfs = fetch_stock_data(stock_symbols)
115
- st.success("Stock data fetched successfully!")
116
 
117
- # Save data
118
- st.write("Storing stock data...")
119
- store_stock_data(stock_dfs)
120
- st.success("Stock data stored successfully!")
121
 
122
- # Load last price
123
- extracted_data = load_stock_data_and_extract_price(Config.STOCK_DATA_DIR)
124
 
125
- # Merge extracted price with main dictionary
126
- merged_stock_data = merge_stock_data_with_price(stock_data, extracted_data)
 
127
 
128
- # Generate prompt for LLM
129
- formatted_prompt = generate_prompt(merged_stock_data)
130
- st.write("Generated Prompt:", formatted_prompt)
131
 
132
- # Invoke LLM
133
- st.write("Invoking LLM...")
134
- try:
135
- portfolio_output = invoke_llm_for_portfolio(formatted_prompt)
136
- st.success("LLM invocation successful!")
137
- st.json(portfolio_output)
138
- except Exception as e:
139
- st.error(f"An error occurred: {e}")
140
 
141
- # Save portfolio output
142
- portfolio_to_json(portfolio_output)
143
 
144
- # Market scenarios extraction
145
- url = "https://www.livemint.com/market/stock-market-news/page-7"
146
- st.write("Extracting market scenarios...")
147
- # context_data = asyncio.run(extract_text_from_website(url))
148
- context_data = scrape_website(url)
149
- print(context_data)
150
- st.success("Market context extracted successfully!")
151
 
152
- # Generate scenario prompt
153
- scenario_prompt = f"""
154
  # TASK: Analyze market context and identify potential market scenarios.
155
 
156
  # CONTEXT:
@@ -165,8 +252,7 @@ if uploaded_file is not None:
165
  {sectors}
166
 
167
  # EXAMPLE:
168
-
169
- json
170
  {{
171
  "market_scenarios": {{
172
  "scenario1": {{
@@ -191,31 +277,257 @@ if uploaded_file is not None:
191
  }}
192
  """
193
 
194
- chat_session = configure_generative_ai()
195
-
 
196
  try:
197
- scenario_response = get_response(chat_session, scenario_prompt)
198
- json_output = extract_json_content(scenario_response.text)
199
- scenario_data = json.loads(json_output)
200
- with open(Config.SCENARIO_OUTPUT_FILE, "w") as f:
201
- json.dump(scenario_data, f, indent=4)
202
- st.success("Market scenarios saved successfully!")
 
 
203
  except Exception as e:
204
- st.error(f"Error: {e}")
205
-
206
- # Monte Carlo Simulation
207
- st.write("Running Monte Carlo Simulation...")
208
- simulation_results = monte_carlo_simulation(portfolio_output, scenario_data)
209
-
210
- # Save simulation results
211
- simulation_results_file = "output_files/simulation_results.json"
212
- os.makedirs(os.path.dirname(simulation_results_file), exist_ok=True)
213
- with open(simulation_results_file, "w") as file:
214
- json.dump(simulation_results, file, indent=4)
215
- st.success("Monte Carlo Simulation completed!")
216
-
217
- # Download Output Files
218
- st.write("Download Output Files")
219
- for file in [Config.OUTPUT_FILE, Config.SCENARIO_OUTPUT_FILE, simulation_results_file]:
220
- with open(file, "rb") as f:
221
- st.download_button(label=f"Download {file.split('/')[-1]}", data=f, file_name=file.split('/')[-1])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
  import os
4
  import json
5
+ import yfinance as yf
6
+ from langchain_core.output_parsers import JsonOutputParser
7
+ from pydantic import BaseModel, Field, ValidationError
8
+ from typing import List, Optional, Dict
9
+ from langchain_groq import ChatGroq
10
+ from dataclasses import dataclass, field
11
  from dotenv import load_dotenv
12
+ 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:
30
+ ALPHA_VANTAGE_API_KEY = os.getenv("ALPHA_VANTAGE_API_KEY")
31
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY")
32
+ STOCK_DATA_DIR = "stock_data_NSE"
33
+ OUTPUT_FILE = "output_files/portfolio.json"
34
+ SECTORS = [
35
+ "Communication Services",
36
+ "Consumer Discretionary",
37
+ "Consumer Staples",
38
+ "Energy",
39
+ "Financials",
40
+ "Health Care",
41
+ "Industrials",
42
+ "Information Technology",
43
+ "Materials",
44
+ "Real Estate",
45
+ "Utilities"
46
+ ]
47
+ GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
48
+
49
+
50
+ # Create output directories if they don't exist
51
+ if not os.path.exists(Config.STOCK_DATA_DIR):
52
+ os.makedirs(Config.STOCK_DATA_DIR)
53
+ if not os.path.exists("output_files"):
54
+ os.makedirs("output_files")
55
+
56
+
57
+ # --------------------- Function from portfolio.py ---------------------
58
+
59
+ def fetch_stock_data(symbols: List[str]) -> Dict[str, pd.DataFrame | None]:
60
+ """Fetches stock data for multiple symbols from Yahoo Finance."""
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
+
76
+
77
+ def store_stock_data(stock_dataframes: Dict[str, pd.DataFrame | None],
78
+ output_path: str = Config.STOCK_DATA_DIR) -> None:
79
+ """Stores stock data to local CSV files."""
80
+ for symbol, data in stock_dataframes.items():
81
+ if data is not 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]]:
94
+ """Loads stock data from CSV files and extracts the most recent (last) day's closing price."""
95
+ all_stock_data = {}
96
+ for filename in os.listdir(output_path_dir):
97
+ if filename.endswith("_daily_data.csv"):
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
113
+
114
+
115
+ def merge_stock_data_with_price(stock_data: Dict, extracted_data: Dict) -> Dict:
116
+ """Merges the extracted price data with the main stock data."""
117
+ merged_stock_data = stock_data.copy()
118
+ for key, value in stock_data.items():
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
+
127
+ def generate_prompt(stock_data: Dict) -> str:
128
+ """Generates a prompt for the language model with all the stock data"""
129
+ prompt_template_with_price = """
130
+ You are a financial analysis expert.
131
+ Please provide a summary of the following stock data, including the company name, stock symbol, and initial purchase price.
132
+
133
+ Stock Data:
134
+ {stock_data}
135
+
136
+ Summary:
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
+
143
+ class Asset(BaseModel):
144
+ """Represents an asset within a portfolio."""
145
+ quantity: int = Field(..., description="The number of shares or units held for this specific asset.")
146
+ initial_price: float = Field(..., description="The initial purchase price per share or unit of this asset.")
147
+ sector: str = Field(..., description=f"""The economic sector of the asset, based on the stock symbol or company name.
148
+ For example, use this {Config.SECTORS}'Financials' for HDFC or JPM, 'consumer' for PG, 'Information Technology' for GOOG. This categorization
149
+ should be done based on the business nature of the company whose stock is traded. For instance,
150
+ if the stock symbol is 'HDFCBANK', the sector is expected to be 'Financials'.""")
151
+
152
+
153
+ class Portfolio(BaseModel):
154
+ """Represents an individual portfolio."""
155
+ name: str = Field(...,
156
+ description="The name given to this portfolio, for example 'Diversified Portfolio'. 'Aggressive Tech Portfolio' ")
157
+ assets: Dict[str, Asset] = Field(..., description="""A dictionary containing the assets within this portfolio. The keys of the dictionary
158
+ are the ticker symbols of the stocks (e.g., 'JPM', 'PG'), and the values are the corresponding
159
+ 'Asset' objects, which define the quantity, initial price, and sector for each asset.
160
+ Example: {'JPM': {'quantity': 150, 'initial_price': 140, 'sector': 'finance'},
161
+ 'PG': {'quantity': 200, 'initial_price': 160, 'sector': 'consumer'}}"""
162
+ )
163
+
164
+
165
+ def invoke_llm_for_portfolio(formatted_prompt: str) -> Portfolio:
166
+ """Invokes the LLM for structured output of the 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"
 
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
208
+ generation_config = {
209
+ "temperature": 1,
210
+ "top_p": 0.95,
211
+ "top_k": 40,
212
+ "max_output_tokens": 8192,
213
+ "response_mime_type": "text/plain",
214
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
 
216
+ model = genai.GenerativeModel(
217
+ model_name="gemini-2.0-flash-exp",
218
+ generation_config=generation_config,
219
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
 
221
+ 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
 
237
+ def invoke_llm_for_scenario(context_data):
238
+ sectors = Config.SECTORS
 
 
 
 
 
239
 
240
+ prompt = f"""
 
241
  # TASK: Analyze market context and identify potential market scenarios.
242
 
243
  # CONTEXT:
 
252
  {sectors}
253
 
254
  # EXAMPLE:
255
+ ```json
 
256
  {{
257
  "market_scenarios": {{
258
  "scenario1": {{
 
277
  }}
278
  """
279
 
280
+ answer = get_response(chat_session, prompt)
281
+ json_output = extract_json_content(answer.text)
282
+ output_file = "output_files/scenario.json"
283
  try:
284
+ analysis_json = json.loads(json_output)
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 ---------------------
298
+ def monte_carlo_simulation(portfolio_data, scenario_data, num_simulations=10000):
299
+ """Performs a Monte Carlo simulation on a portfolio based on market scenarios."""
300
+ portfolio = portfolio_data
301
+ scenarios = scenario_data["market_scenarios"]
302
+
303
+ results = {}
304
+
305
+ for scenario_key, scenario_details in scenarios.items():
306
+ scenario_name = scenario_details["name"]
307
+ sector_impacts = scenario_details.get("sector_impact", {})
308
+ results[scenario_name] = {
309
+ "portfolio_values": [],
310
+ "average_return": 0,
311
+ "std_dev_return": 0,
312
+ "percentiles": {},
313
+ }
314
+
315
+ for _ in range(num_simulations):
316
+ portfolio_value = 0
317
+ for asset_name, asset_details in portfolio["assets"].items():
318
+ sector = asset_details["sector"]
319
+ quantity = asset_details["quantity"]
320
+ initial_price = asset_details["initial_price"]
321
+
322
+ price_change_percentage = 0
323
+ if isinstance(sector_impacts, dict) and sector in sector_impacts:
324
+ price_change_percentage = np.random.normal(loc=sector_impacts[sector] / 100, scale=0.1)
325
+ # Calculate the new price
326
+ new_price = initial_price * (1 + price_change_percentage)
327
+
328
+ portfolio_value += new_price * quantity
329
+ results[scenario_name]["portfolio_values"].append(portfolio_value)
330
+
331
+ # Calculate Results
332
+ portfolio_values = results[scenario_name]["portfolio_values"]
333
+ initial_portfolio_value = sum(
334
+ asset["quantity"] * asset["initial_price"] for asset in portfolio["assets"].values())
335
+ returns = [(value - initial_portfolio_value) / initial_portfolio_value for value in portfolio_values]
336
+
337
+ results[scenario_name]["average_return"] = np.mean(returns)
338
+ results[scenario_name]["std_dev_return"] = np.std(returns)
339
+ results[scenario_name]["percentiles"] = {
340
+ 5: np.percentile(returns, 5),
341
+ 25: np.percentile(returns, 25),
342
+ 50: np.percentile(returns, 50),
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
+
361
+ def calculate_scenario_magnitudes(portfolio_data, scenario_data, saved_dataframes):
362
+ scenario_results = {}
363
+
364
+ for scenario_name, scenario_details in scenario_data["market_scenarios"].items():
365
+ impacted_sectors = scenario_details["sector_impact"]
366
+
367
+ # Filter assets in the impacted sectors
368
+ relevant_assets = [
369
+ symbol
370
+ for symbol, details in portfolio_data["assets"].items()
371
+ if details["sector"] in impacted_sectors
372
+ ]
373
+
374
+ # Calculate magnitudes for the scenario
375
+ sector_magnitudes = {}
376
+ for symbol in relevant_assets:
377
+ df = saved_dataframes[symbol]
378
+ sector = portfolio_data["assets"][symbol]["sector"]
379
+
380
+ # Calculate magnitude as the absolute difference between first and last Close price
381
+ magnitude = abs(df["Close"].iloc[-2] - df["Close"].iloc[-1])
382
+
383
+ # Aggregate by sector
384
+ if sector not in sector_magnitudes:
385
+ sector_magnitudes[sector] = 0
386
+ sector_magnitudes[sector] += magnitude
387
+
388
+ # Calculate aggregated magnitude for the scenario
389
+ aggregated_magnitude = sum(sector_magnitudes.values())
390
+
391
+ # Store results
392
+ scenario_results[scenario_name] = {
393
+ "individual_magnitudes": sector_magnitudes,
394
+ "aggregated_magnitude": aggregated_magnitude,
395
+ }
396
+
397
+ return scenario_results
398
+
399
+
400
+ def update_scenario_data(scenario_data, scenario_results):
401
+ for scenario_id, results in scenario_results.items():
402
+ # Update the sector impacts to include individual magnitudes
403
+ scenario_data["market_scenarios"][scenario_id]["sector_impact"] = results["individual_magnitudes"]
404
+ # Update aggregated magnitude
405
+ scenario_data["market_scenarios"][scenario_id]["aggregated_magnitude"] = results["aggregated_magnitude"]
406
+
407
+ return scenario_data
408
+
409
+
410
+ # --------------------- Streamlit App ---------------------
411
+ def main():
412
+ st.title("Portfolio Analysis and Simulation App")
413
+
414
+ # Initialize session state for data storage
415
+ if 'stock_data' not in st.session_state:
416
+ st.session_state['stock_data'] = {}
417
+
418
+ if 'saved_dataframes' not in st.session_state:
419
+ st.session_state['saved_dataframes'] = None
420
+
421
+ if 'portfolio_data' not in st.session_state:
422
+ st.session_state['portfolio_data'] = {}
423
+
424
+ if 'scenario_data' not in st.session_state:
425
+ st.session_state['scenario_data'] = {}
426
+
427
+ if 'simulation_results' not in st.session_state:
428
+ st.session_state['simulation_results'] = {}
429
+
430
+ # Input for stock data as a JSON file
431
+ st.header("1. Upload Portfolio Data (JSON)")
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
490
+ url = st.text_input("Enter Livemint URL (e.g. https://www.livemint.com/market/stock-market-news/page-7)",
491
+ value="https://www.livemint.com/market/stock-market-news/page-7")
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__":
533
+ main()