molba2see commited on
Commit
e24bcda
·
verified ·
1 Parent(s): 579d747

Update src/streamlit_app.py

Browse files

사용자 투자 성향을 파이차트 위에도 적어주기(Srisk랑 같이)
재생성 버튼 두 개 되는 오류 수정

Files changed (1) hide show
  1. src/streamlit_app.py +42 -22
src/streamlit_app.py CHANGED
@@ -1,4 +1,5 @@
1
  import os
 
2
  os.environ['STREAMLIT_HOME'] = '/tmp/streamlit'
3
 
4
  os.environ['HF_HOME'] = '/tmp/hf_cache'
@@ -89,10 +90,17 @@ def generate_llm_reports(portfolio_list, override_style=None):
89
  if override_style:
90
  investor_style = override_style
91
  st.toast(f"'{investor_style}' 스타일(수동)로 재생성 시작...")
 
 
 
 
92
  else:
93
  srisk, investor_style = classify_investment_style(full_market_df, portfolio_list)
94
  st.toast(f"srisk: {srisk:.2f} '{investor_style}' 스타일(자동)로 생성 시작...")
95
-
 
 
 
96
  reports = {}
97
 
98
  for item in portfolio_list:
@@ -193,23 +201,24 @@ def generate_llm_report(investor_style, ticker):
193
  return result
194
 
195
 
196
- # ----------------------------------------------------------------------
197
  # 1. 세션 상태(Session State) 초기화
198
- # ----------------------------------------------------------------------
199
- # st.session_state : 스트림릿이 재실행되어도 값을 유지하는 마법의 변수
200
  if 'portfolio' not in st.session_state:
201
  st.session_state.portfolio = [] # 사용자의 포트폴리오를 저장할 리스트
202
  if 'last_report' not in st.session_state:
203
  st.session_state.last_report = None # 생성된 보고서를 저장할 변수
204
 
 
 
 
 
 
205
  if 'peft_model' not in st.session_state:
206
  st.session_state.peft_model = None
207
  if 'tokenizer' not in st.session_state:
208
  st.session_state.tokenizer = None
209
 
210
- # ----------------------------------------------------------------------
211
  # 2. 페이지 기본 설정
212
- # ----------------------------------------------------------------------
213
  st.set_page_config(page_title="AI 주식 포트폴리오 분석", layout="wide")
214
  st.title("🤖 AI 주식 포트폴리오 보고서 생성기")
215
  st.write("NASDAQ 100 종목을 검색하여 포트폴리오를 구성하고, 맞춤형 AI 보고서를 받아보세요.")
@@ -218,9 +227,7 @@ TICKER_OPTIONS_LIST, DISPLAY_TO_TICKER_MAP, TICKER_TO_PRICE_MAP = load_ticker_da
218
  company = load_company_metrics()
219
  full_market_df = load_full_df() # (Srisk 모듈용 데이터)
220
 
221
- # ----------------------------------------------------------------------
222
  # 3. 입력 섹션 (종목 추가)
223
- # ----------------------------------------------------------------------
224
  st.subheader("1. 보유 종목 추가하기")
225
 
226
  # 컬럼을 나눠서 UI를 깔끔하게 구성
@@ -255,15 +262,34 @@ if st.button("➕ 포트폴리오에 추가", use_container_width=True):
255
  else:
256
  st.warning("종목, 수량을 모두 올바르게 입력하세요.")
257
 
258
- # ----------------------------------------------------------------------
259
  # 4. 포트폴리오 요약 및 보고서 생성 (스케치 레이아웃)
260
- # ----------------------------------------------------------------------
261
  st.subheader("2. 포트폴리오 요약 및 보고서 생성")
262
-
263
  col_chart, col_controls = st.columns(2, gap="large")
264
 
265
  with col_chart:
266
  st.markdown("### 📊 포트폴리오 구성")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267
  if st.session_state.portfolio:
268
  df = pd.DataFrame(st.session_state.portfolio)
269
 
@@ -299,7 +325,6 @@ with col_controls:
299
  )
300
 
301
  if not df.equals(edited_df):
302
- # 삭제되거나 수정된 DataFrame을 다시 세션 상태(list of dicts)로 변환
303
  st.session_state.portfolio = edited_df.to_dict('records')
304
  st.toast("포트폴리오가 수정(삭제)되었습니다.")
305
  st.rerun()
@@ -307,12 +332,14 @@ with col_controls:
307
  if st.button("🔄 포트폴리오 전체 초기화", use_container_width=True, type="secondary"):
308
  st.session_state.portfolio = []
309
  st.session_state.last_report = None
 
 
 
 
310
  st.toast("포트폴리오가 초기화되었습니다.")
311
  st.rerun()
312
 
313
- # ----------------------------------------------------------------------
314
  # 5. 보고서 생성 버튼 (메인 LLM 호출)
315
- # ----------------------------------------------------------------------
316
  if st.button("🚀 AI 보고서 생성하기", type="primary", use_container_width=True, disabled=(not st.session_state.portfolio)):
317
  if st.session_state.peft_model and st.session_state.tokenizer:
318
  with st.spinner("AI가 포트폴리오를 분석하고 보고서를 작성 중입니다..."):
@@ -324,9 +351,7 @@ with col_controls:
324
  st.warning("모델이 아직 로드 중입니다. 잠시 후 다시 시도해주세요.")
325
  st.divider()
326
 
327
- # ----------------------------------------------------------------------
328
  # 6. 보고서 재생성 버튼 (메인 LLM 호출)
329
- # ----------------------------------------------------------------------
330
  if st.session_state.last_report:
331
  st.markdown("##### 🔄 다른 성향으로 보고서 다시 뽑기")
332
 
@@ -341,7 +366,7 @@ with col_controls:
341
  )
342
 
343
  with col_regen:
344
- if st.button(f"'{new_style}' 스타일로 재생성", use_container_width=True):
345
  with st.spinner(f"'{new_style}' 스타일로 보고서를 다시 작성 중입니다..."):
346
  regenerated_reports = generate_llm_reports(
347
  st.session_state.portfolio,
@@ -350,9 +375,7 @@ with col_controls:
350
  st.session_state.last_report = regenerated_reports
351
  st.rerun() # 화면을 즉시 새로고침
352
 
353
- # ----------------------------------------------------------------------
354
  # 7. 보고서 출력 섹션
355
- # ----------------------------------------------------------------------
356
  st.divider()
357
 
358
  if st.session_state.last_report:
@@ -368,10 +391,7 @@ if st.session_state.last_report:
368
  else:
369
  st.info("보고서를 생성하면 이 곳에 결과가 표시됩니다.")
370
 
371
- # ----------------------------------------------------------------------
372
  # 8. (신규) LLM 모델 로딩 (모든 UI를 그린 후 마지막에 실행)
373
- # ----------------------------------------------------------------------
374
-
375
  # peft_model, tokenizer를 st.session_state로 관리
376
  if 'peft_model' not in st.session_state:
377
  st.session_state.peft_model = None
 
1
  import os
2
+
3
  os.environ['STREAMLIT_HOME'] = '/tmp/streamlit'
4
 
5
  os.environ['HF_HOME'] = '/tmp/hf_cache'
 
90
  if override_style:
91
  investor_style = override_style
92
  st.toast(f"'{investor_style}' 스타일(수동)로 재생성 시작...")
93
+
94
+ st.session_state.investor_style = investor_style
95
+ st.session_state.srisk = None # 수동 선택 시 Srisk는 N/A 처리
96
+
97
  else:
98
  srisk, investor_style = classify_investment_style(full_market_df, portfolio_list)
99
  st.toast(f"srisk: {srisk:.2f} '{investor_style}' 스타일(자동)로 생성 시작...")
100
+
101
+ st.session_state.investor_style = investor_style
102
+ st.session_state.srisk = srisk
103
+
104
  reports = {}
105
 
106
  for item in portfolio_list:
 
201
  return result
202
 
203
 
 
204
  # 1. 세션 상태(Session State) 초기화
205
+ # st.session_state : 스트림릿이 재실행되어도 값을 유지하는 변수
 
206
  if 'portfolio' not in st.session_state:
207
  st.session_state.portfolio = [] # 사용자의 포트폴리오를 저장할 리스트
208
  if 'last_report' not in st.session_state:
209
  st.session_state.last_report = None # 생성된 보고서를 저장할 변수
210
 
211
+ if 'srisk' not in st.session_state:
212
+ st.session_state.srisk = None
213
+ if 'investor_style' not in st.session_state:
214
+ st.session_state.investor_style = None
215
+
216
  if 'peft_model' not in st.session_state:
217
  st.session_state.peft_model = None
218
  if 'tokenizer' not in st.session_state:
219
  st.session_state.tokenizer = None
220
 
 
221
  # 2. 페이지 기본 설정
 
222
  st.set_page_config(page_title="AI 주식 포트폴리오 분석", layout="wide")
223
  st.title("🤖 AI 주식 포트폴리오 보고서 생성기")
224
  st.write("NASDAQ 100 종목을 검색하여 포트폴리오를 구성하고, 맞춤형 AI 보고서를 받아보세요.")
 
227
  company = load_company_metrics()
228
  full_market_df = load_full_df() # (Srisk 모듈용 데이터)
229
 
 
230
  # 3. 입력 섹션 (종목 추가)
 
231
  st.subheader("1. 보유 종목 추가하기")
232
 
233
  # 컬럼을 나눠서 UI를 깔끔하게 구성
 
262
  else:
263
  st.warning("종목, 수량을 모두 올바르게 입력하세요.")
264
 
 
265
  # 4. 포트폴리오 요약 및 보고서 생성 (스케치 레이아웃)
 
266
  st.subheader("2. 포트폴리오 요약 및 보고서 생성")
 
267
  col_chart, col_controls = st.columns(2, gap="large")
268
 
269
  with col_chart:
270
  st.markdown("### 📊 포트폴리오 구성")
271
+
272
+ if st.session_state.get("investor_style"):
273
+ style = st.session_state.investor_style
274
+ srisk = st.session_state.srisk
275
+
276
+ # (수정) 스타일에 따라 이모지(색상 동그라미) 추가
277
+ if style == "SAFE":
278
+ style_display = f"🔵 {style}" # 파란색
279
+ elif style == "NEUTRAL":
280
+ style_display = f"🟢 {style}" # 연두색
281
+ elif style == "RISKY":
282
+ style_display = f"🔴 {style}" # 빨간색
283
+ else:
284
+ style_display = style # 기본값
285
+
286
+ if srisk is not None:
287
+ # 자동 분석 (Srisk가 있음)
288
+ st.metric(label="AI 분석 투자 성향", value=style, delta=f"S-Risk: {srisk:.2f}")
289
+ else:
290
+ # 수동 선택 (Srisk가 None)
291
+ st.metric(label="선택된 투자 성향", value=style, delta="수동 선택")
292
+
293
  if st.session_state.portfolio:
294
  df = pd.DataFrame(st.session_state.portfolio)
295
 
 
325
  )
326
 
327
  if not df.equals(edited_df):
 
328
  st.session_state.portfolio = edited_df.to_dict('records')
329
  st.toast("포트폴리오가 수정(삭제)되었습니다.")
330
  st.rerun()
 
332
  if st.button("🔄 포트폴리오 전체 초기화", use_container_width=True, type="secondary"):
333
  st.session_state.portfolio = []
334
  st.session_state.last_report = None
335
+
336
+ st.session_state.srisk = None
337
+ st.session_state.investor_style = None
338
+
339
  st.toast("포트폴리오가 초기화되었습니다.")
340
  st.rerun()
341
 
 
342
  # 5. 보고서 생성 버튼 (메인 LLM 호출)
 
343
  if st.button("🚀 AI 보고서 생성하기", type="primary", use_container_width=True, disabled=(not st.session_state.portfolio)):
344
  if st.session_state.peft_model and st.session_state.tokenizer:
345
  with st.spinner("AI가 포트폴리오를 분석하고 보고서를 작성 중입니다..."):
 
351
  st.warning("모델이 아직 로드 중입니다. 잠시 후 다시 시도해주세요.")
352
  st.divider()
353
 
 
354
  # 6. 보고서 재생성 버튼 (메인 LLM 호출)
 
355
  if st.session_state.last_report:
356
  st.markdown("##### 🔄 다른 성향으로 보고서 다시 뽑기")
357
 
 
366
  )
367
 
368
  with col_regen:
369
+ if st.button(f"'{new_style}' 스타일로 재생성", use_container_width=True, "my_regen_button"):
370
  with st.spinner(f"'{new_style}' 스타일로 보고서를 다시 작성 중입니다..."):
371
  regenerated_reports = generate_llm_reports(
372
  st.session_state.portfolio,
 
375
  st.session_state.last_report = regenerated_reports
376
  st.rerun() # 화면을 즉시 새로고침
377
 
 
378
  # 7. 보고서 출력 섹션
 
379
  st.divider()
380
 
381
  if st.session_state.last_report:
 
391
  else:
392
  st.info("보고서를 생성하면 이 곳에 결과가 표시됩니다.")
393
 
 
394
  # 8. (신규) LLM 모델 로딩 (모든 UI를 그린 후 마지막에 실행)
 
 
395
  # peft_model, tokenizer를 st.session_state로 관리
396
  if 'peft_model' not in st.session_state:
397
  st.session_state.peft_model = None