AlanRex commited on
Commit
4d718ba
·
verified ·
1 Parent(s): 8324e30

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +41 -20
app.py CHANGED
@@ -15,10 +15,19 @@ from plotly.subplots import make_subplots
15
  import re
16
  from bs4 import BeautifulSoup
17
  import requests
 
18
 
19
  # 引用您組員的預測器程式
20
  from Bert_predict import BertPredictor
21
 
 
 
 
 
 
 
 
 
22
  # 台股代號對應表 (移除台指期,因為它現在是獨立區塊)
23
  TAIWAN_STOCKS = {
24
  '元大台灣50': '0050.TW',
@@ -189,7 +198,6 @@ def get_pmi_data():
189
  print(f"無法獲取 PMI 資料: {str(e)}")
190
  return pd.DataFrame()
191
 
192
- # ========================= GEMINI 整合 START =========================
193
  def generate_gemini_analysis(stock_name, stock_symbol, period, data):
194
  """
195
  使用 Gemini API 生成基本面和市場展望分析。
@@ -202,7 +210,6 @@ def generate_gemini_analysis(stock_name, stock_symbol, period, data):
202
  genai.configure(api_key=api_key)
203
  model = genai.GenerativeModel('gemini-1.5-flash')
204
 
205
- # 準備傳送給模型的數據
206
  price_change = ((data['Close'].iloc[-1] - data['Close'].iloc[0]) / data['Close'].iloc[0]) * 100
207
  rsi_current = data['RSI'].iloc[-1]
208
  macd_current = data['MACD'].iloc[-1]
@@ -229,7 +236,7 @@ def generate_gemini_analysis(stock_name, stock_symbol, period, data):
229
 
230
  2. **市場展望與投資建議 (約 150 字):**
231
  - 基於上述所有資訊,提供對該股票的短期和中期市場展望。
232
- - 提出具體的投資建議,例如:適合何種類型的投資人、潛在的風險點。
233
  - 請直接提供分析內容,不要包含任何問候語。
234
 
235
  **輸出格式:**
@@ -244,13 +251,13 @@ def generate_gemini_analysis(stock_name, stock_symbol, period, data):
244
  market_outlook = parts[1].strip()
245
  return dcc.Markdown(fundamental_analysis), dcc.Markdown(market_outlook)
246
  else:
247
- return "無法解析 Gemini 回應", response.text
 
248
 
249
  except Exception as e:
250
  error_message = f"呼叫 Gemini API 時發生錯誤: {str(e)}"
251
  print(error_message)
252
- return error_message, "請檢查後台日誌或 API 金鑰設定"
253
- # ========================== GEMINI 整合 END ==========================
254
 
255
  # 建立 Dash 應用程式
256
  app = dash.Dash(__name__, suppress_callback_exceptions=True)
@@ -314,7 +321,7 @@ app.layout = html.Div([
314
  html.Label("時間範圍:"),
315
  dcc.Dropdown(id='period-dropdown',
316
  options=[{'label': '1個月', 'value': '1mo'},{'label': '3個月', 'value': '3mo'},{'label': '6個月', 'value': '6mo'},{'label': '1年', 'value': '1y'},{'label': '2年', 'value': '2y'}],
317
- value='1mo', style={'margin-bottom': '10px'}) # 預設改為 1mo
318
  ], style={'width': '30%', 'display': 'inline-block', 'margin-left': '5%', 'vertical-align': 'top'}),
319
  html.Div([
320
  html.Label("圖表類型:"),
@@ -593,7 +600,7 @@ def update_business_climate_chart(selected_stock):
593
  fig.update_layout(title="台灣景氣燈號走勢", xaxis_title='日期', yaxis_title='燈號分數', height=300, yaxis=dict(range=[0, 40]))
594
  return fig
595
 
596
- # ========================= MODIFIED SECTION START =========================
597
  @app.callback(
598
  [dash.dependencies.Output('technical-analysis-text', 'children'),
599
  dash.dependencies.Output('fundamental-analysis-text', 'children'),
@@ -602,14 +609,29 @@ def update_business_climate_chart(selected_stock):
602
  dash.dependencies.Input('period-dropdown', 'value')]
603
  )
604
  def update_analysis_text(selected_stock, period):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
605
  data = get_stock_data(selected_stock, period)
606
  stock_name = [name for name, symbol in TAIWAN_STOCKS.items() if symbol == selected_stock][0]
607
- if data.empty or len(data) < 20: # 確保有足夠資料計算指標
608
  return "資料不足,無法分析", "資料不足,無法分析", "資料不足,無法分析"
609
 
610
  data = calculate_technical_indicators(data)
611
 
612
- # 1. 技術面分析 (保留客觀數據呈現)
613
  price_change = ((data['Close'].iloc[-1] - data['Close'].iloc[0]) / data['Close'].iloc[0]) * 100
614
  rsi_current = data['RSI'].iloc[-1] if not pd.isna(data['RSI'].iloc[-1]) else 50
615
  macd_current = data['MACD'].iloc[-1] if not pd.isna(data['MACD'].iloc[-1]) else 0
@@ -621,17 +643,16 @@ def update_analysis_text(selected_stock, period):
621
  html.P([html.Strong("MACD 指標:"), f"MACD 快線 ({macd_current:.3f}) 目前", html.Span("高於" if macd_current > macd_signal_current else "低於", style={'color': 'red' if macd_current > macd_signal_current else 'green', 'font-weight': 'bold'}), f" Signal 慢線 ({macd_signal_current:.3f}),", f"顯示市場動能偏向{'多頭' if macd_current > macd_signal_current else '空頭'}。"]),
622
  ])
623
 
624
- # 2. 基本面與展望分析 (呼叫 Gemini)
625
- # 顯示“正在生成…”提示,改善使用者體驗
626
- loading_text = html.Div([
627
- dcc.Loading(id="loading-analysis", type="dots", children=[html.Div(id="loading-output")])
628
- ])
629
 
630
- try:
631
- fundamental_text, market_outlook_text = generate_gemini_analysis(stock_name, selected_stock, period, data)
632
- except Exception as e:
633
- fundamental_text = f"生成分析時發生錯誤: {e}"
634
- market_outlook_text = "請檢查 API 金鑰或網路連線。"
 
 
635
 
636
  return technical_text, fundamental_text, market_outlook_text
637
  # ========================== MODIFIED SECTION END ==========================
 
15
  import re
16
  from bs4 import BeautifulSoup
17
  import requests
18
+ import time # 引用 time 模組以處理時間戳
19
 
20
  # 引用您組員的預測器程式
21
  from Bert_predict import BertPredictor
22
 
23
+ # ========================= CACHE 設定 START =========================
24
+ # 分析結果的快取字典
25
+ ANALYSIS_CACHE = {}
26
+ # 快取有效時間(秒),例如:4 小時 = 4 * 60 * 60 = 14400 秒
27
+ CACHE_DURATION_SECONDS = 8 * 60 * 60
28
+ # ========================== CACHE 設定 END ==========================
29
+
30
+
31
  # 台股代號對應表 (移除台指期,因為它現在是獨立區塊)
32
  TAIWAN_STOCKS = {
33
  '元大台灣50': '0050.TW',
 
198
  print(f"無法獲取 PMI 資料: {str(e)}")
199
  return pd.DataFrame()
200
 
 
201
  def generate_gemini_analysis(stock_name, stock_symbol, period, data):
202
  """
203
  使用 Gemini API 生成基本面和市場展望分析。
 
210
  genai.configure(api_key=api_key)
211
  model = genai.GenerativeModel('gemini-1.5-flash')
212
 
 
213
  price_change = ((data['Close'].iloc[-1] - data['Close'].iloc[0]) / data['Close'].iloc[0]) * 100
214
  rsi_current = data['RSI'].iloc[-1]
215
  macd_current = data['MACD'].iloc[-1]
 
236
 
237
  2. **市場展望與投資建議 (約 150 字):**
238
  - 基於上述所有資訊,提供對該股票的短期和中期市場展望。
239
+ - 提出具體的投資建議,例如:適合何種類型的投資人、潛在的風險點、以及建議的觀察價位區間或進出場策略。
240
  - 請直接提供分析內容,不要包含任何問候語。
241
 
242
  **輸出格式:**
 
251
  market_outlook = parts[1].strip()
252
  return dcc.Markdown(fundamental_analysis), dcc.Markdown(market_outlook)
253
  else:
254
+ # Fallback for unexpected response format
255
+ return dcc.Markdown("無法解析 Gemini 回應,請稍後再試。"), dcc.Markdown(response.text)
256
 
257
  except Exception as e:
258
  error_message = f"呼叫 Gemini API 時發生錯誤: {str(e)}"
259
  print(error_message)
260
+ return dcc.Markdown(error_message), dcc.Markdown("請檢查後台日誌或 API 金鑰設定")
 
261
 
262
  # 建立 Dash 應用程式
263
  app = dash.Dash(__name__, suppress_callback_exceptions=True)
 
321
  html.Label("時間範圍:"),
322
  dcc.Dropdown(id='period-dropdown',
323
  options=[{'label': '1個月', 'value': '1mo'},{'label': '3個月', 'value': '3mo'},{'label': '6個月', 'value': '6mo'},{'label': '1年', 'value': '1y'},{'label': '2年', 'value': '2y'}],
324
+ value='1mo', style={'margin-bottom': '10px'})
325
  ], style={'width': '30%', 'display': 'inline-block', 'margin-left': '5%', 'vertical-align': 'top'}),
326
  html.Div([
327
  html.Label("圖表類型:"),
 
600
  fig.update_layout(title="台灣景氣燈號走勢", xaxis_title='日期', yaxis_title='燈號分數', height=300, yaxis=dict(range=[0, 40]))
601
  return fig
602
 
603
+ # ========================= MODIFIED SECTION START (CACHE INTEGRATED) =========================
604
  @app.callback(
605
  [dash.dependencies.Output('technical-analysis-text', 'children'),
606
  dash.dependencies.Output('fundamental-analysis-text', 'children'),
 
609
  dash.dependencies.Input('period-dropdown', 'value')]
610
  )
611
  def update_analysis_text(selected_stock, period):
612
+ # 建立快取的唯一鍵值
613
+ cache_key = f"{selected_stock}-{period}"
614
+ current_time = time.time()
615
+
616
+ # 1. 檢查快取
617
+ if cache_key in ANALYSIS_CACHE:
618
+ cached_data = ANALYSIS_CACHE[cache_key]
619
+ if current_time - cached_data['timestamp'] < CACHE_DURATION_SECONDS:
620
+ print(f"從快取載入分析: {cache_key}")
621
+ # 直接回傳快取的內容
622
+ return cached_data['technical'], cached_data['fundamental'], cached_data['outlook']
623
+
624
+ print(f"重新生成分析: {cache_key}")
625
+ # --- 如果快取沒有,才繼續執行以下程式 ---
626
+
627
  data = get_stock_data(selected_stock, period)
628
  stock_name = [name for name, symbol in TAIWAN_STOCKS.items() if symbol == selected_stock][0]
629
+ if data.empty or len(data) < 20:
630
  return "資料不足,無法分析", "資料不足,無法分析", "資料不足,無法分析"
631
 
632
  data = calculate_technical_indicators(data)
633
 
634
+ # 2. 技術面分析
635
  price_change = ((data['Close'].iloc[-1] - data['Close'].iloc[0]) / data['Close'].iloc[0]) * 100
636
  rsi_current = data['RSI'].iloc[-1] if not pd.isna(data['RSI'].iloc[-1]) else 50
637
  macd_current = data['MACD'].iloc[-1] if not pd.isna(data['MACD'].iloc[-1]) else 0
 
643
  html.P([html.Strong("MACD 指標:"), f"MACD 快線 ({macd_current:.3f}) 目前", html.Span("高於" if macd_current > macd_signal_current else "低於", style={'color': 'red' if macd_current > macd_signal_current else 'green', 'font-weight': 'bold'}), f" Signal 慢線 ({macd_signal_current:.3f}),", f"顯示市場動能偏向{'多頭' if macd_current > macd_signal_current else '空頭'}。"]),
644
  ])
645
 
646
+ # 3. 基本面與展望分析 (呼叫 Gemini)
647
+ fundamental_text, market_outlook_text = generate_gemini_analysis(stock_name, selected_stock, period, data)
 
 
 
648
 
649
+ # 4. 將新產生的結果存入快取
650
+ ANALYSIS_CACHE[cache_key] = {
651
+ 'technical': technical_text,
652
+ 'fundamental': fundamental_text,
653
+ 'outlook': market_outlook_text,
654
+ 'timestamp': current_time
655
+ }
656
 
657
  return technical_text, fundamental_text, market_outlook_text
658
  # ========================== MODIFIED SECTION END ==========================