Darshan03 commited on
Commit
2bb6ae6
·
verified ·
1 Parent(s): e3f821b

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +397 -0
app.py ADDED
@@ -0,0 +1,397 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import os
3
+ import json
4
+ import pickle
5
+ from dotenv import load_dotenv
6
+ import asyncio
7
+ import google.generativeai as genai
8
+ from io import BytesIO
9
+ from portfolio import (
10
+ fetch_stock_data,
11
+ store_stock_data,
12
+ load_stock_data_and_extract_price,
13
+ portfolio_to_json,
14
+ merge_stock_data_with_price,
15
+ generate_prompt,
16
+ invoke_llm_for_portfolio
17
+ )
18
+ from scenario import (
19
+ extract_text_from_website,
20
+ get_response,
21
+ extract_json_content
22
+ )
23
+ from simluation_data import monte_carlo_simulation
24
+ # add this part here
25
+ import platform
26
+ if platform.system() == "Windows":
27
+ asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
28
+
29
+ # Load environment variables from .env
30
+ load_dotenv()
31
+
32
+ # Configuration
33
+ class Config:
34
+ ALPHA_VANTAGE_API_KEY = os.getenv("ALPHA_VANTAGE_API_KEY")
35
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY")
36
+ GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
37
+ STOCK_DATA_DIR = "stock_data_NSE"
38
+ OUTPUT_DIR = "output_files"
39
+ OUTPUT_FILE = os.path.join(OUTPUT_DIR, "portfolio.json")
40
+ SCENARIO_OUTPUT_FILE = os.path.join(OUTPUT_DIR, "scenario.json")
41
+ UPDATED_SCENARIO_FILE = os.path.join(OUTPUT_DIR, "updated_scenario_data.json")
42
+ SIMULATION_RESULTS_FILE = os.path.join(OUTPUT_DIR, "simulation_results.json")
43
+ SAVED_DATA_FRAMES_FILE = os.path.join(OUTPUT_DIR, "saved_dataframes.pkl")
44
+ SECTORS = [
45
+ "Communication Services",
46
+ "Consumer Discretionary",
47
+ "Consumer Staples",
48
+ "Energy",
49
+ "Financials",
50
+ "Health Care",
51
+ "Industrials",
52
+ "Information Technology",
53
+ "Materials",
54
+ "Real Estate",
55
+ "Utilities"
56
+ ]
57
+
58
+
59
+ # Helper function to load stock data from a JSON file
60
+ def load_stock_data(filepath):
61
+ """Loads stock data from a JSON file.
62
+
63
+ Args:
64
+ filepath (str): The path to the JSON file.
65
+
66
+ Returns:
67
+ dict: A dictionary containing the stock data, or None if an error occurs.
68
+ """
69
+ try:
70
+ with open(filepath, 'r') as f:
71
+ data = json.load(f)
72
+ return data
73
+ except FileNotFoundError:
74
+ st.error(f"Error: File not found at '{filepath}'")
75
+ return None
76
+ except json.JSONDecodeError:
77
+ st.error(f"Error: Invalid JSON format in '{filepath}'")
78
+ return None
79
+ except Exception as e:
80
+ st.error(f"An unexpected error occurred: {e}")
81
+ return None
82
+
83
+
84
+ def configure_generative_ai():
85
+ """Configures the generative AI model and starts a chat session."""
86
+ genai.configure(api_key=Config.GOOGLE_API_KEY)
87
+
88
+ generation_config = {
89
+ "temperature": 1,
90
+ "top_p": 0.95,
91
+ "top_k": 40,
92
+ "max_output_tokens": 8192,
93
+ "response_mime_type": "text/plain",
94
+ }
95
+
96
+ model = genai.GenerativeModel(
97
+ model_name="gemini-2.0-flash-exp",
98
+ generation_config=generation_config,
99
+ )
100
+
101
+ return model.start_chat()
102
+
103
+
104
+ # Function to save dataframes to a pickle file
105
+ def save_dataframes(dataframes_dict, filename):
106
+ with open(filename, 'wb') as file:
107
+ pickle.dump(dataframes_dict, file)
108
+ st.success(f"DataFrames successfully saved to {filename}.")
109
+
110
+
111
+ # Function to load dataframes from a pickle file
112
+ def load_dataframes(filename):
113
+ try:
114
+ with open(filename, 'rb') as file:
115
+ saved_dataframes = pickle.load(file)
116
+ st.success(f"DataFrames successfully loaded from {filename}.")
117
+ return saved_dataframes
118
+ except FileNotFoundError:
119
+ st.error(f"File {filename} not found.")
120
+ return None
121
+
122
+
123
+ def process_data(stock_data):
124
+ """Main function to process stock data and generate output files."""
125
+ # 1. Fetch stock data
126
+ stock_symbols = [value["symbol"] for value in stock_data.values()]
127
+ with st.spinner("Fetching stock data..."):
128
+ stock_dfs = fetch_stock_data(stock_symbols)
129
+
130
+ # Save DataFrames in a dictionary for future use
131
+ saved_dataframes = {}
132
+ if stock_dfs:
133
+ for symbol, df in stock_dfs.items():
134
+ if df is not None:
135
+ # Save DataFrame in the variable
136
+ saved_dataframes[symbol] = df
137
+ st.success(f"Data for '{symbol}' loaded into variable.")
138
+ else:
139
+ st.error(f"No data found for '{symbol}'")
140
+ else:
141
+ st.error("Error occurred during fetching data. DataFrames are not returned.")
142
+ return False
143
+
144
+ save_dataframes(saved_dataframes, Config.SAVED_DATA_FRAMES_FILE)
145
+
146
+ # 2. Store data
147
+ with st.spinner("Storing stock data..."):
148
+ store_stock_data(stock_dfs)
149
+
150
+ # 3. Load the last price
151
+ with st.spinner("Loading stock prices..."):
152
+ extracted_data = load_stock_data_and_extract_price(Config.STOCK_DATA_DIR)
153
+
154
+ # 4. Merge extracted price with the main dictionary
155
+ merged_stock_data = merge_stock_data_with_price(stock_data, extracted_data)
156
+
157
+ # 5. Generate prompt for LLM
158
+ formatted_prompt = generate_prompt(merged_stock_data)
159
+ st.write("LLM Prompt:")
160
+ st.text(formatted_prompt)
161
+
162
+ # 6. Invoke LLM
163
+ with st.spinner("Invoking LLM for portfolio analysis..."):
164
+ try:
165
+ portfolio_output = invoke_llm_for_portfolio(formatted_prompt)
166
+ st.write("LLM Portfolio Output:")
167
+ st.text(portfolio_output)
168
+ except Exception as e:
169
+ st.error(f"An unexpected error occurred during the LLM invocation: {e}")
170
+ return False
171
+ else:
172
+ # 7. Save portfolio output to JSON
173
+ portfolio_to_json(portfolio_output)
174
+
175
+ # 8. Generate market scenarios
176
+ url = "https://www.livemint.com/market/stock-market-news/page-7"
177
+ with st.spinner("Extracting text from website..."):
178
+ context_data = asyncio.run(extract_text_from_website(url))
179
+
180
+ chat_session = configure_generative_ai()
181
+
182
+ scenario_prompt = f"""
183
+ # TASK: Analyze market context and identify potential market scenarios.
184
+
185
+ # CONTEXT:
186
+ {context_data}
187
+ # END CONTEXT
188
+
189
+ # INSTRUCTION: Based on the provided market context, analyze and identify up to three plausible market scenarios.
190
+ # For each scenario, determine its name (e.g., "Moderate Downturn"), the general market direction ("up" or "down"), a major trigger point that could cause the scenario to unfold, and a list of sectors that would be significantly impacted. Each 'sector_impact' list should have less than or equal to 4 sectors.
191
+
192
+ # OUTPUT FORMAT: Provide the analysis in JSON format with the following structure.
193
+ # Use the sector names provided:
194
+ {Config.SECTORS}
195
+
196
+ # EXAMPLE:
197
+ ```json
198
+ {{
199
+ "market_scenarios": {{
200
+ "scenario1": {{
201
+ "name": "Moderate Downturn",
202
+ "direction": "down",
203
+ "trigger": "Interest rate hike",
204
+ "sector_impact": [
205
+ "Financials",
206
+ "Energy"
207
+ ]
208
+ }},
209
+ "scenario2": {{
210
+ "name": "Bullish Growth",
211
+ "direction": "up",
212
+ "trigger": "Successful vaccine rollout",
213
+ "sector_impact": [
214
+ "Health Care",
215
+ "Information Technology"
216
+ ]
217
+ }}
218
+ }}
219
+ }}
220
+ ```
221
+ """
222
+
223
+ with st.spinner("Generating market scenarios..."):
224
+ try:
225
+ scenario_response = get_response(chat_session, scenario_prompt)
226
+ json_output = extract_json_content(scenario_response.text)
227
+ scenario_data = json.loads(json_output)
228
+ os.makedirs(os.path.dirname(Config.SCENARIO_OUTPUT_FILE), exist_ok=True)
229
+ with open(Config.SCENARIO_OUTPUT_FILE, "w") as f:
230
+ json.dump(scenario_data, f, indent=4)
231
+ st.success(f"Market scenarios saved to '{Config.SCENARIO_OUTPUT_FILE}'")
232
+ except json.JSONDecodeError:
233
+ st.error("Error: Could not decode the output from the model into JSON format.")
234
+ return False
235
+ except Exception as e:
236
+ st.error(f"Error: {e}")
237
+ return False
238
+
239
+
240
+ # Simulation
241
+ with open(Config.SCENARIO_OUTPUT_FILE) as f:
242
+ scenario_data = json.load(f)
243
+
244
+ with open(Config.OUTPUT_FILE) as f:
245
+ portfolio_data = json.load(f)
246
+ saved_dataframes = load_dataframes(Config.SAVED_DATA_FRAMES_FILE)
247
+ if not saved_dataframes:
248
+ return False
249
+
250
+ # Placeholder for storing results
251
+ scenario_results = {}
252
+
253
+ # Process each scenario
254
+ for scenario_name, scenario_details in scenario_data["market_scenarios"].items():
255
+ impacted_sectors = scenario_details["sector_impact"]
256
+
257
+ # Filter assets in the impacted sectors
258
+ relevant_assets = [
259
+ symbol
260
+ for symbol, details in portfolio_data["assets"].items()
261
+ if details["sector"] in impacted_sectors
262
+ ]
263
+
264
+ # Calculate magnitudes for the scenario
265
+ sector_magnitudes = {}
266
+ for symbol in relevant_assets:
267
+ df = saved_dataframes[symbol]
268
+ sector = portfolio_data["assets"][symbol]["sector"]
269
+
270
+ # Calculate magnitude as the absolute difference between first and last Close price
271
+ magnitude = abs(df["Close"].iloc[-2] - df["Close"].iloc[-1])
272
+
273
+ # Aggregate by sector
274
+ if sector not in sector_magnitudes:
275
+ sector_magnitudes[sector] = 0
276
+ sector_magnitudes[sector] += magnitude
277
+
278
+ # Calculate aggregated magnitude for the scenario
279
+ aggregated_magnitude = sum(sector_magnitudes.values())
280
+
281
+ # Store results
282
+ scenario_results[scenario_name] = {
283
+ "individual_magnitudes": sector_magnitudes,
284
+ "aggregated_magnitude": aggregated_magnitude,
285
+ }
286
+
287
+ # Display results
288
+ st.write("## Scenario Analysis")
289
+ for scenario_name, results in scenario_results.items():
290
+ st.write(f"### Scenario: {scenario_name}")
291
+ st.write("Individual Sector Magnitudes:")
292
+ for sector, magnitude in results["individual_magnitudes"].items():
293
+ st.write(f" {sector}: {magnitude:.2f}")
294
+ st.write(f"Aggregated Magnitude: {results['aggregated_magnitude']:.2f}")
295
+
296
+ # Integrate calculated results into scenario data
297
+ for scenario_id, results in scenario_results.items():
298
+ # Update the sector impacts to include individual magnitudes
299
+ scenario_data["market_scenarios"][scenario_id]["sector_impact"] = results["individual_magnitudes"]
300
+ # Update aggregated magnitude
301
+ scenario_data["market_scenarios"][scenario_id]["aggregated_magnitude"] = results["aggregated_magnitude"]
302
+
303
+ # Save the updated scenario data to a local JSON file
304
+ os.makedirs(os.path.dirname(Config.UPDATED_SCENARIO_FILE), exist_ok=True)
305
+ with open(Config.UPDATED_SCENARIO_FILE, "w") as file:
306
+ json.dump(scenario_data, file, indent=4)
307
+
308
+ st.success(f"Updated scenario data saved to '{Config.UPDATED_SCENARIO_FILE}' successfully!")
309
+
310
+ # Run Monte Carlo simulation
311
+ with st.spinner("Running Monte Carlo simulation..."):
312
+ simulation_results = monte_carlo_simulation(portfolio_data, scenario_data)
313
+
314
+ # Save simulation results to a local JSON file
315
+ os.makedirs(os.path.dirname(Config.SIMULATION_RESULTS_FILE), exist_ok=True)
316
+ with open(Config.SIMULATION_RESULTS_FILE, "w") as file:
317
+ json.dump(simulation_results, file, indent=4)
318
+
319
+ st.success(f"Simulation results saved to '{Config.SIMULATION_RESULTS_FILE}' successfully!")
320
+
321
+ # Display simulation results
322
+ st.write("## Simulation Results")
323
+ for scenario_name, results in simulation_results.items():
324
+ st.write(f"### Scenario: {scenario_name}")
325
+ st.write(f" Average Return: {results['average_return']:.4f}")
326
+ st.write(f" Std Dev Return: {results['std_dev_return']:.4f}")
327
+ st.write(" Return Percentiles:")
328
+ for percentile, value in results["percentiles"].items():
329
+ st.write(f" {percentile}th: {value:.4f}")
330
+ st.write("-" * 40)
331
+
332
+ return True
333
+
334
+
335
+ def main():
336
+ st.title("Portfolio Analysis App")
337
+ # Create output directory if it doesn't exist
338
+ if not os.path.exists(Config.OUTPUT_DIR):
339
+ os.makedirs(Config.OUTPUT_DIR)
340
+ # File upload
341
+ uploaded_file = st.file_uploader("Upload your portfolio JSON file", type=["json"])
342
+
343
+ if uploaded_file is not None:
344
+ try:
345
+ # Save the uploaded file to the data folder
346
+ file_path = os.path.join("data", uploaded_file.name)
347
+ with open(file_path, "wb") as f:
348
+ f.write(uploaded_file.read())
349
+ st.success(f"File uploaded successfully: {uploaded_file.name} to {file_path}")
350
+ stock_data = load_stock_data(file_path)
351
+ except Exception as e:
352
+ st.error(f"Error processing uploaded file: {e}")
353
+ return
354
+
355
+
356
+ if stock_data:
357
+ if process_data(stock_data):
358
+ st.write("## Download Output Files")
359
+ # Download button for portfolio.json
360
+ with open(Config.OUTPUT_FILE, "rb") as f:
361
+ st.download_button(
362
+ label="Download Portfolio Output (portfolio.json)",
363
+ data=f,
364
+ file_name="portfolio.json",
365
+ mime="application/json",
366
+ )
367
+
368
+ # Download button for scenario.json
369
+ with open(Config.SCENARIO_OUTPUT_FILE, "rb") as f:
370
+ st.download_button(
371
+ label="Download Scenario Output (scenario.json)",
372
+ data=f,
373
+ file_name="scenario.json",
374
+ mime="application/json",
375
+ )
376
+ # Download button for updated_scenario_data.json
377
+ with open(Config.UPDATED_SCENARIO_FILE, "rb") as f:
378
+ st.download_button(
379
+ label="Download Updated Scenario (updated_scenario_data.json)",
380
+ data=f,
381
+ file_name="updated_scenario_data.json",
382
+ mime="application/json",
383
+ )
384
+
385
+ # Download button for simulation_results.json
386
+ with open(Config.SIMULATION_RESULTS_FILE, "rb") as f:
387
+ st.download_button(
388
+ label="Download Simulation Results (simulation_results.json)",
389
+ data=f,
390
+ file_name="simulation_results.json",
391
+ mime="application/json",
392
+ )
393
+ else:
394
+ st.error("Error occurred during processing.")
395
+
396
+ if __name__ == "__main__":
397
+ main()