AlanRex commited on
Commit
5dfd2d3
·
verified ·
1 Parent(s): ac0c9bd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +46 -26
app.py CHANGED
@@ -219,24 +219,30 @@ def get_exchange_rate():
219
  return 31.5
220
 
221
  def simple_statistical_predict(data, predict_days=5):
222
- """【備用模型】簡化的統計預測模型。"""
223
  if len(data) < 60:
224
  return None
225
 
226
  prices = data['Close'].values
 
 
227
  ma_short = np.mean(prices[-5:])
228
  ma_medium = np.mean(prices[-20:])
229
  ma_long = np.mean(prices[-60:])
230
  recent_trend = np.polyfit(range(20), prices[-20:], 1)[0]
231
  volatility = np.std(prices[-20:]) / np.mean(prices[-20:])
 
232
  base_change = recent_trend * predict_days
233
  trend_factor = 1.0 + (0.02 if ma_short > ma_medium > ma_long else -0.02 if ma_short < ma_medium < ma_long else 0)
234
  noise_factor = np.random.normal(1, volatility * 0.1)
235
- predicted_price = prices[-1] * trend_factor + base_change + (prices[-1] * noise_factor * 0.01)
236
- change_pct = ((predicted_price - prices[-1]) / prices[-1]) * 100
 
 
 
237
  return {
238
  'predicted_price': predicted_price,
239
- 'change_pct': change_pct,
240
  'confidence': max(0.6, 1 - volatility * 2)
241
  }
242
 
@@ -289,9 +295,10 @@ def calculate_new_features(df):
289
  def advanced_xgboost_predict(predict_days=5):
290
  """
291
  【進階模型】使用 XGBoost 模型進行預測 - 新特徵版本
 
292
  """
293
  try:
294
- print(f"開始使用 XGBoost 模型進行 {predict_days} 天預測(新特徵版本)...")
295
 
296
  # 初始化 XGBoost 模型
297
  xgb_model = XGBoostModel()
@@ -328,7 +335,7 @@ def advanced_xgboost_predict(predict_days=5):
328
  # 取得昨日收盤價
329
  yesterday_close = latest_data['Close']
330
 
331
- # 【【【修正】】】特徵列表,確保與模型訓練時完全一致
332
  new_feature_columns = [
333
  'return_t-1',
334
  'return_t-5',
@@ -383,7 +390,7 @@ def advanced_xgboost_predict(predict_days=5):
383
 
384
  feature_names.append(feature)
385
 
386
- # 【【【修正】】】 按照模型訓練的順序添加剩餘特徵
387
  # 7. dji_return_t-1
388
  features_list.append(dji_return)
389
  feature_names.append('dji_return_t-1')
@@ -421,7 +428,7 @@ def advanced_xgboost_predict(predict_days=5):
421
 
422
  # 詳細的資料驗證日誌
423
  print("=" * 60)
424
- print("XGBoost 模型輸入特徵檢查報告 (新特徵版本)")
425
  print("=" * 60)
426
 
427
  print(f"總特徵數量: {len(features_list)} 個")
@@ -431,7 +438,7 @@ def advanced_xgboost_predict(predict_days=5):
431
  print("\n特徵狀態詳情:")
432
  for i, (name, value) in enumerate(zip(feature_names, features_list)):
433
  status = feature_status.get(name, {})
434
- status_symbol = "✓正常" if status.get('is_real', False) else "⚠預設值"
435
  print(f" [{i+1:2d}] {name:18s}: {value:12.6f} ({status_symbol})")
436
 
437
  # 統計完整性
@@ -456,12 +463,12 @@ def advanced_xgboost_predict(predict_days=5):
456
  # 進行預測
457
  predictions = xgb_model.predict('xgboost_model', input_df)
458
 
459
- # 根據預測天數選擇對應的預測值
460
  pred_mapping = {
461
- 1: 'Close_t0_pred',
462
- 5: 'Close_t5_pred',
463
- 10: 'Close_t10_pred',
464
- 20: 'Close_t20_pred'
465
  }
466
 
467
  # 找到最接近的預測天數
@@ -469,21 +476,24 @@ def advanced_xgboost_predict(predict_days=5):
469
  closest_day = min(available_days, key=lambda x: abs(x - predict_days))
470
  pred_key = pred_mapping[closest_day]
471
 
472
- predicted_price = predictions[pred_key]
 
 
 
473
  current_price = latest_data['Close']
474
- change_pct = ((predicted_price - current_price) / current_price) * 100
475
 
476
  print(f"XGBoost 預測完成:")
477
  print(f"- 預測天數: {predict_days} (使用 {closest_day} 天模型)")
478
  print(f"- 當前價格: {current_price:.2f}")
479
- print(f"- 預測價格: {predicted_price:.2f}")
480
- print(f"- 預測變化: {change_pct:+.2f}%")
481
  print(f"- 使用特徵數: {len(features_list)} 個")
482
  print(f"- 特徵完整性: {completeness:.1f}%")
483
 
484
  return {
485
- 'predicted_price': predicted_price,
486
- 'change_pct': change_pct,
487
  'confidence': max(0.6, min(0.85, completeness / 100)) # 根據特徵完整性調整信心度
488
  }
489
 
@@ -789,7 +799,7 @@ app.layout = html.Div([
789
  ], style={'margin-top': '30px','padding': '20px','background': 'white','border-radius': '10px','box-shadow': '0 2px 10px rgba(0,0,0,0.1)'}),
790
  ])
791
 
792
- # 回調函數區域
793
  @app.callback(
794
  [dash.dependencies.Output('taiex-prediction-results', 'children'),
795
  dash.dependencies.Output('taiex-prediction-chart', 'figure')],
@@ -804,32 +814,42 @@ def update_taiex_prediction(predict_days):
804
 
805
  if final_prediction is None: return html.Div("資料不足,無法進行預測"), {}
806
  current_price, last_date = data['Close'].iloc[-1], data.index[-1]
 
 
807
  predicted_price, change_pct, confidence = final_prediction['predicted_price'], final_prediction['change_pct'], final_prediction['confidence']
808
 
 
809
  prediction_paths = {1: [1], 5: [1, 5], 10: [1, 5, 10], 20: [1, 10, 20], 60: [1, 10, 20, 60]}
810
  intervals_to_predict = prediction_paths.get(predict_days, [predict_days])
811
  prediction_dates, prediction_prices = [last_date], [current_price]
812
 
813
  for days in intervals_to_predict:
814
- # === 修改點:迴圈內也使用統一的預測控制器 ===
815
  interim_prediction = get_prediction(data, days)
816
  if interim_prediction:
817
  prediction_dates.append(last_date + timedelta(days=days))
818
  prediction_prices.append(interim_prediction['predicted_price'])
819
 
820
- # 後續繪圖邏輯不變
821
  color, arrow = ('red', '📈') if change_pct >= 0 else ('green', '📉')
822
  result_card = html.Div([
823
  html.H4(f"{predict_days}日後預測結果", style={'margin': '0 0 15px 0', 'color': 'white'}),
824
- html.Div([html.Span(f"{arrow} ", style={'font-size': '24px'}), html.Span(f"{change_pct:+.2f}%", style={'font-size': '28px','font-weight': 'bold','color': color})], style={'margin': '10px 0'}),
825
- html.P(f"目前價格: {current_price:.2f}", style={'margin': '5px 0'}), html.P(f"預測價格: {predicted_price:.2f}", style={'margin': '5px 0'}),
 
 
 
 
 
826
  html.P(f"信心度: {confidence:.1%}", style={'margin': '5px 0', 'font-size': '14px'})
827
  ], style={'background': 'rgba(255,255,255,0.1)','padding': '20px','border-radius': '10px','border': '1px solid rgba(255,255,255,0.2)'})
 
 
828
  fig = go.Figure()
829
  recent_data = data.tail(30)
830
  fig.add_trace(go.Scatter(x=recent_data.index, y=recent_data['Close'], mode='lines', name='歷史價格', line=dict(color='#FFA726', width=2)))
831
  fig.add_trace(go.Scatter(x=prediction_dates, y=prediction_prices, mode='lines+markers', name=f'{predict_days}日預測路徑', line=dict(color=color, width=3, dash='dash'), marker=dict(size=8)))
832
- fig.update_layout(title=f'台指期 {predict_days}日預測走勢', xaxis_title='日期', yaxis_title='指數點位', height=350, plot_bgcolor='rgba(0,0,0,0)', paper_bgcolor='rgba(0,0,0,0)', font=dict(color='white'))
 
833
  return result_card, fig
834
 
835
  @app.callback(
 
219
  return 31.5
220
 
221
  def simple_statistical_predict(data, predict_days=5):
222
+ """【備用模型】簡化的統計預測模型 - 更新為輸出漲幅百分比格式。"""
223
  if len(data) < 60:
224
  return None
225
 
226
  prices = data['Close'].values
227
+ current_price = prices[-1]
228
+
229
  ma_short = np.mean(prices[-5:])
230
  ma_medium = np.mean(prices[-20:])
231
  ma_long = np.mean(prices[-60:])
232
  recent_trend = np.polyfit(range(20), prices[-20:], 1)[0]
233
  volatility = np.std(prices[-20:]) / np.mean(prices[-20:])
234
+
235
  base_change = recent_trend * predict_days
236
  trend_factor = 1.0 + (0.02 if ma_short > ma_medium > ma_long else -0.02 if ma_short < ma_medium < ma_long else 0)
237
  noise_factor = np.random.normal(1, volatility * 0.1)
238
+ predicted_price = current_price * trend_factor + base_change + (current_price * noise_factor * 0.01)
239
+
240
+ # 【重要更新】計算漲幅百分比
241
+ change_pct = ((predicted_price - current_price) / current_price) * 100
242
+
243
  return {
244
  'predicted_price': predicted_price,
245
+ 'change_pct': change_pct, # 現在這個值是真正的漲幅百分比
246
  'confidence': max(0.6, 1 - volatility * 2)
247
  }
248
 
 
295
  def advanced_xgboost_predict(predict_days=5):
296
  """
297
  【進階模型】使用 XGBoost 模型進行預測 - 新特徵版本
298
+ 【重要更新】現在輸出漲幅百分比而非絕對價格
299
  """
300
  try:
301
+ print(f"開始使用 XGBoost 模型進行 {predict_days} 天預測(漲幅百分比版本)...")
302
 
303
  # 初始化 XGBoost 模型
304
  xgb_model = XGBoostModel()
 
335
  # 取得昨日收盤價
336
  yesterday_close = latest_data['Close']
337
 
338
+ # 特徵列表,確保與模型訓練時完全一致
339
  new_feature_columns = [
340
  'return_t-1',
341
  'return_t-5',
 
390
 
391
  feature_names.append(feature)
392
 
393
+ # 按照模型訓練的順序添加剩餘特徵
394
  # 7. dji_return_t-1
395
  features_list.append(dji_return)
396
  feature_names.append('dji_return_t-1')
 
428
 
429
  # 詳細的資料驗證日誌
430
  print("=" * 60)
431
+ print("XGBoost 模型輸入特徵檢查報告 (漲幅百分比版本)")
432
  print("=" * 60)
433
 
434
  print(f"總特徵數量: {len(features_list)} 個")
 
438
  print("\n特徵狀態詳情:")
439
  for i, (name, value) in enumerate(zip(feature_names, features_list)):
440
  status = feature_status.get(name, {})
441
+ status_symbol = "✓正常" if status.get('is_real', False) else "⚠ 預設值"
442
  print(f" [{i+1:2d}] {name:18s}: {value:12.6f} ({status_symbol})")
443
 
444
  # 統計完整性
 
463
  # 進行預測
464
  predictions = xgb_model.predict('xgboost_model', input_df)
465
 
466
+ # 【重要更新】處理新的漲幅百分比輸出格式
467
  pred_mapping = {
468
+ 1: 'Change_pct_t1_pred', # 1天後漲幅%
469
+ 5: 'Change_pct_t5_pred', # 5天後漲幅%
470
+ 10: 'Change_pct_t10_pred', # 10天後漲幅%
471
+ 20: 'Change_pct_t20_pred' # 20天後漲幅%
472
  }
473
 
474
  # 找到最接近的預測天數
 
476
  closest_day = min(available_days, key=lambda x: abs(x - predict_days))
477
  pred_key = pred_mapping[closest_day]
478
 
479
+ # 【關鍵修改】現在直接取得漲幅百分比
480
+ predicted_change_pct = predictions[pred_key]
481
+
482
+ # 【新增】為了兼容性,計算預測價格(僅供參考)
483
  current_price = latest_data['Close']
484
+ predicted_price = current_price * (1 + predicted_change_pct / 100)
485
 
486
  print(f"XGBoost 預測完成:")
487
  print(f"- 預測天數: {predict_days} (使用 {closest_day} 天模型)")
488
  print(f"- 當前價格: {current_price:.2f}")
489
+ print(f"- 預測漲幅: {predicted_change_pct:+.2f}%")
490
+ print(f"- 預測價格: {predicted_price:.2f} (參考)")
491
  print(f"- 使用特徵數: {len(features_list)} 個")
492
  print(f"- 特徵完整性: {completeness:.1f}%")
493
 
494
  return {
495
+ 'predicted_price': predicted_price, # 為了兼容現有代碼
496
+ 'change_pct': predicted_change_pct, # 【新增】直接的漲幅百分比
497
  'confidence': max(0.6, min(0.85, completeness / 100)) # 根據特徵完整性調整信心度
498
  }
499
 
 
799
  ], style={'margin-top': '30px','padding': '20px','background': 'white','border-radius': '10px','box-shadow': '0 2px 10px rgba(0,0,0,0.1)'}),
800
  ])
801
 
802
+ # 修改台指期預測的回調函數
803
  @app.callback(
804
  [dash.dependencies.Output('taiex-prediction-results', 'children'),
805
  dash.dependencies.Output('taiex-prediction-chart', 'figure')],
 
814
 
815
  if final_prediction is None: return html.Div("資料不足,無法進行預測"), {}
816
  current_price, last_date = data['Close'].iloc[-1], data.index[-1]
817
+
818
+ # 【重要更新】現在 change_pct 已經是正確的漲幅百分比
819
  predicted_price, change_pct, confidence = final_prediction['predicted_price'], final_prediction['change_pct'], final_prediction['confidence']
820
 
821
+ # 生成預測路徑(為了圖表顯示)
822
  prediction_paths = {1: [1], 5: [1, 5], 10: [1, 5, 10], 20: [1, 10, 20], 60: [1, 10, 20, 60]}
823
  intervals_to_predict = prediction_paths.get(predict_days, [predict_days])
824
  prediction_dates, prediction_prices = [last_date], [current_price]
825
 
826
  for days in intervals_to_predict:
 
827
  interim_prediction = get_prediction(data, days)
828
  if interim_prediction:
829
  prediction_dates.append(last_date + timedelta(days=days))
830
  prediction_prices.append(interim_prediction['predicted_price'])
831
 
832
+ # 後續繪圖邏輯不變,但現在 change_pct 是真正的漲幅百分比
833
  color, arrow = ('red', '📈') if change_pct >= 0 else ('green', '📉')
834
  result_card = html.Div([
835
  html.H4(f"{predict_days}日後預測結果", style={'margin': '0 0 15px 0', 'color': 'white'}),
836
+ html.Div([
837
+ html.Span(f"{arrow} ", style={'font-size': '24px'}),
838
+ html.Span(f"{change_pct:+.2f}%", style={'font-size': '28px','font-weight': 'bold','color': color})
839
+ ], style={'margin': '10px 0'}),
840
+ html.P(f"目前價格: {current_price:.2f}", style={'margin': '5px 0'}),
841
+ html.P(f"預測價格: {predicted_price:.2f}", style={'margin': '5px 0'}),
842
+ html.P(f"預測漲幅: {change_pct:+.2f}%", style={'margin': '5px 0', 'font-weight': 'bold'}), # 【新增】
843
  html.P(f"信心度: {confidence:.1%}", style={'margin': '5px 0', 'font-size': '14px'})
844
  ], style={'background': 'rgba(255,255,255,0.1)','padding': '20px','border-radius': '10px','border': '1px solid rgba(255,255,255,0.2)'})
845
+
846
+ # 繪圖部分保持不變
847
  fig = go.Figure()
848
  recent_data = data.tail(30)
849
  fig.add_trace(go.Scatter(x=recent_data.index, y=recent_data['Close'], mode='lines', name='歷史價格', line=dict(color='#FFA726', width=2)))
850
  fig.add_trace(go.Scatter(x=prediction_dates, y=prediction_prices, mode='lines+markers', name=f'{predict_days}日預測路徑', line=dict(color=color, width=3, dash='dash'), marker=dict(size=8)))
851
+ fig.update_layout(title=f'台指期 {predict_days}日預測走勢 (預測漲幅: {change_pct:+.2f}%)', xaxis_title='日期', yaxis_title='指數點位', height=350, plot_bgcolor='rgba(0,0,0,0)', paper_bgcolor='rgba(0,0,0,0)', font=dict(color='white'))
852
+
853
  return result_card, fig
854
 
855
  @app.callback(