Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -29,7 +29,7 @@ from model_predictor import XGBoostModel
|
|
| 29 |
# 【【【模型切換開關】】】
|
| 30 |
# False: 使用簡易統計模型 (預設)
|
| 31 |
# True: 使用 model_predictor.py 中的進階 XGBoost 模型
|
| 32 |
-
USE_ADVANCED_MODEL =
|
| 33 |
|
| 34 |
# ========================= CACHE 設定 START =========================
|
| 35 |
# 分析結果的快取字典
|
|
@@ -242,7 +242,7 @@ def simple_statistical_predict(data, predict_days=5):
|
|
| 242 |
|
| 243 |
def advanced_xgboost_predict(predict_days=5):
|
| 244 |
"""
|
| 245 |
-
【進階模型】使用 XGBoost 模型進行預測
|
| 246 |
"""
|
| 247 |
try:
|
| 248 |
print(f"開始使用 XGBoost 模型進行 {predict_days} 天預測...")
|
|
@@ -259,12 +259,6 @@ def advanced_xgboost_predict(predict_days=5):
|
|
| 259 |
# 計算技術指標
|
| 260 |
taiex_data = calculate_technical_indicators(taiex_data)
|
| 261 |
|
| 262 |
-
# 獲取美股市場數據
|
| 263 |
-
us_market = get_us_market_data()
|
| 264 |
-
|
| 265 |
-
# 獲取匯率數據
|
| 266 |
-
exchange_rate = get_exchange_rate()
|
| 267 |
-
|
| 268 |
# 獲取新聞情緒分數
|
| 269 |
try:
|
| 270 |
if predictor is not None:
|
|
@@ -279,127 +273,71 @@ def advanced_xgboost_predict(predict_days=5):
|
|
| 279 |
# 準備特徵數據 (使用最新的數據點)
|
| 280 |
latest_data = taiex_data.iloc[-1]
|
| 281 |
|
| 282 |
-
#
|
| 283 |
-
# 先檢查每個技術指標是否存在並記錄狀態
|
| 284 |
tech_indicators_status = {}
|
| 285 |
|
| 286 |
-
#
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
'
|
| 290 |
-
'
|
| 291 |
-
'
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
macd_hist_value = latest_data['MACD_Histogram'] if not pd.isna(latest_data['MACD_Histogram']) else 0
|
| 298 |
-
|
| 299 |
-
tech_indicators_status['MACD'] = {
|
| 300 |
-
'value': macd_value,
|
| 301 |
-
'is_real': not pd.isna(latest_data['MACD']),
|
| 302 |
-
'source': 'calculated' if not pd.isna(latest_data['MACD']) else 'default'
|
| 303 |
-
}
|
| 304 |
-
tech_indicators_status['MACD_Signal'] = {
|
| 305 |
-
'value': macd_signal_value,
|
| 306 |
-
'is_real': not pd.isna(latest_data['MACD_Signal']),
|
| 307 |
-
'source': 'calculated' if not pd.isna(latest_data['MACD_Signal']) else 'default'
|
| 308 |
-
}
|
| 309 |
-
tech_indicators_status['MACD_Histogram'] = {
|
| 310 |
-
'value': macd_hist_value,
|
| 311 |
-
'is_real': not pd.isna(latest_data['MACD_Histogram']),
|
| 312 |
-
'source': 'calculated' if not pd.isna(latest_data['MACD_Histogram']) else 'default'
|
| 313 |
-
}
|
| 314 |
-
|
| 315 |
-
# KD 指標檢查
|
| 316 |
-
k_value = latest_data['K'] if not pd.isna(latest_data['K']) else 50
|
| 317 |
-
d_value = latest_data['D'] if not pd.isna(latest_data['D']) else 50
|
| 318 |
-
|
| 319 |
-
tech_indicators_status['K'] = {
|
| 320 |
-
'value': k_value,
|
| 321 |
-
'is_real': not pd.isna(latest_data['K']),
|
| 322 |
-
'source': 'calculated' if not pd.isna(latest_data['K']) else 'default'
|
| 323 |
-
}
|
| 324 |
-
tech_indicators_status['D'] = {
|
| 325 |
-
'value': d_value,
|
| 326 |
-
'is_real': not pd.isna(latest_data['D']),
|
| 327 |
-
'source': 'calculated' if not pd.isna(latest_data['D']) else 'default'
|
| 328 |
}
|
| 329 |
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
'value': adx_value,
|
| 347 |
-
'is_real': not pd.isna(latest_data['ADX']),
|
| 348 |
-
'source': 'calculated' if not pd.isna(latest_data['ADX']) else 'default'
|
| 349 |
-
}
|
| 350 |
|
| 351 |
-
#
|
| 352 |
features_list = [
|
| 353 |
-
latest_data['Close'],
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
macd_signal_value, # MACDsign
|
| 365 |
-
macd_hist_value, # MACDvol
|
| 366 |
-
k_value, # K
|
| 367 |
-
d_value, # D
|
| 368 |
-
plus_di_value, # +DI
|
| 369 |
-
minus_di_value, # -DI
|
| 370 |
-
adx_value, # ADX
|
| 371 |
-
15, # business_climate (手動填入值)
|
| 372 |
-
46.7 # PMI (手動填入值)
|
| 373 |
]
|
| 374 |
|
| 375 |
-
#
|
| 376 |
column_names = [
|
| 377 |
-
'close', '
|
| 378 |
-
'
|
| 379 |
-
'+DI', '-DI', 'ADX', 'business_climate', 'PMI'
|
| 380 |
]
|
| 381 |
|
|
|
|
| 382 |
input_df = pd.DataFrame([features_list], columns=column_names)
|
| 383 |
|
| 384 |
# 詳細的資料驗證日誌
|
| 385 |
print("=" * 50)
|
| 386 |
-
print("XGBoost
|
| 387 |
print("=" * 50)
|
| 388 |
|
| 389 |
-
#
|
| 390 |
-
print("
|
| 391 |
-
print(f"
|
| 392 |
-
print(f" 成交量 (volume): {latest_data['Volume']:,.0f}")
|
| 393 |
-
print(f" 匯率 (rate): {exchange_rate:.4f}")
|
| 394 |
-
|
| 395 |
-
# 美股指數
|
| 396 |
-
print("\n🇺🇸 美股指數數據:")
|
| 397 |
-
for key, value in us_market.items():
|
| 398 |
-
status = "✅ 正常" if value > 0 else "⚠️ 可能異常(=0)"
|
| 399 |
-
print(f" {key}: {value:.2f} {status}")
|
| 400 |
-
|
| 401 |
-
# 新聞情緒
|
| 402 |
-
print(f"\n📰 新聞情緒 (NEWS): {sentiment_score_raw:.6f}")
|
| 403 |
if sentiment_score_raw == 0:
|
| 404 |
print(" ⚠️ 新聞情緒分數為0,可能無新聞數據")
|
| 405 |
else:
|
|
@@ -407,32 +345,28 @@ def advanced_xgboost_predict(predict_days=5):
|
|
| 407 |
|
| 408 |
# 技術指標詳細狀態
|
| 409 |
print("\n📈 技術指標狀態:")
|
| 410 |
-
for indicator,
|
|
|
|
| 411 |
status_symbol = "✅" if status['is_real'] else "⚠️"
|
| 412 |
-
source_info = "
|
| 413 |
-
print(f" {indicator}: {status['value']
|
| 414 |
-
|
| 415 |
-
# 手動填入數據
|
| 416 |
-
print("\n🔧 手動填入數據:")
|
| 417 |
-
print(f" business_climate: 15 ✅")
|
| 418 |
-
print(f" PMI: 46.7 ✅")
|
| 419 |
|
| 420 |
-
#
|
| 421 |
real_indicators = sum(1 for status in tech_indicators_status.values() if status['is_real'])
|
| 422 |
total_indicators = len(tech_indicators_status)
|
| 423 |
completeness = (real_indicators / total_indicators) * 100
|
| 424 |
|
| 425 |
-
print(f"\n📋
|
| 426 |
print(f" 實際計算指標: {real_indicators}/{total_indicators} ({completeness:.1f}%)")
|
| 427 |
-
if completeness <
|
| 428 |
-
print(" ⚠️ 警告:超過
|
| 429 |
else:
|
| 430 |
-
print(" ✅
|
| 431 |
|
| 432 |
-
#
|
| 433 |
print(f"\n🔢 完整特徵向量 (共{len(features_list)}個特徵):")
|
| 434 |
for i, (name, value) in enumerate(zip(column_names, features_list)):
|
| 435 |
-
print(f" [{i:2d}] {name:
|
| 436 |
|
| 437 |
print("=" * 50)
|
| 438 |
|
|
@@ -453,7 +387,7 @@ def advanced_xgboost_predict(predict_days=5):
|
|
| 453 |
pred_key = pred_mapping[closest_day]
|
| 454 |
|
| 455 |
predicted_price = predictions[pred_key]
|
| 456 |
-
current_price =
|
| 457 |
change_pct = ((predicted_price - current_price) / current_price) * 100
|
| 458 |
|
| 459 |
print(f"XGBoost 預測完成:")
|
|
@@ -461,6 +395,7 @@ def advanced_xgboost_predict(predict_days=5):
|
|
| 461 |
print(f"- 當前價格: {current_price:.2f}")
|
| 462 |
print(f"- 預測價格: {predicted_price:.2f}")
|
| 463 |
print(f"- 預測變化: {change_pct:+.2f}%")
|
|
|
|
| 464 |
|
| 465 |
return {
|
| 466 |
'predicted_price': predicted_price,
|
|
@@ -619,12 +554,12 @@ def generate_gemini_analysis(stock_name, stock_symbol, period, data):
|
|
| 619 |
**你的任務:**
|
| 620 |
1. **基本面分析 (約 150 字):**
|
| 621 |
- 評論這家公司的產業地位、近期營運亮點或挑戰。
|
| 622 |
-
- 提及任何可能影響其基本面的關鍵因素 (
|
| 623 |
- 請用專業、客觀的語氣撰寫。
|
| 624 |
|
| 625 |
2. **市場展望與投資建議 (約 150 字):**
|
| 626 |
- 基於上述所有資訊,提供對該股票的短期和中期市場展望。
|
| 627 |
-
-
|
| 628 |
- 請直接提供分析內容,不要包含任何問候語。
|
| 629 |
|
| 630 |
**輸出格式:**
|
|
|
|
| 29 |
# 【【【模型切換開關】】】
|
| 30 |
# False: 使用簡易統計模型 (預設)
|
| 31 |
# True: 使用 model_predictor.py 中的進階 XGBoost 模型
|
| 32 |
+
USE_ADVANCED_MODEL = False
|
| 33 |
|
| 34 |
# ========================= CACHE 設定 START =========================
|
| 35 |
# 分析結果的快取字典
|
|
|
|
| 242 |
|
| 243 |
def advanced_xgboost_predict(predict_days=5):
|
| 244 |
"""
|
| 245 |
+
【進階模型】使用 XGBoost 模型進行預測 - 簡化版 (11個特徵)
|
| 246 |
"""
|
| 247 |
try:
|
| 248 |
print(f"開始使用 XGBoost 模型進行 {predict_days} 天預測...")
|
|
|
|
| 259 |
# 計算技術指標
|
| 260 |
taiex_data = calculate_technical_indicators(taiex_data)
|
| 261 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 262 |
# 獲取新聞情緒分數
|
| 263 |
try:
|
| 264 |
if predictor is not None:
|
|
|
|
| 273 |
# 準備特徵數據 (使用最新的數據點)
|
| 274 |
latest_data = taiex_data.iloc[-1]
|
| 275 |
|
| 276 |
+
# 技術指標檢查和狀態記錄
|
|
|
|
| 277 |
tech_indicators_status = {}
|
| 278 |
|
| 279 |
+
# 檢查各技術指標並記錄狀態
|
| 280 |
+
indicators_map = {
|
| 281 |
+
'RSI': (latest_data['RSI'], 50),
|
| 282 |
+
'MACD': (latest_data['MACD'], 0),
|
| 283 |
+
'MACDsign': (latest_data['MACD_Signal'], 0),
|
| 284 |
+
'MACDvol': (latest_data['MACD_Histogram'], 0),
|
| 285 |
+
'K': (latest_data['K'], 50),
|
| 286 |
+
'D': (latest_data['D'], 50),
|
| 287 |
+
'+DI': (latest_data['+DI'], 25),
|
| 288 |
+
'-DI': (latest_data['-DI'], 25),
|
| 289 |
+
'ADX': (latest_data['ADX'], 25),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 290 |
}
|
| 291 |
|
| 292 |
+
processed_values = {}
|
| 293 |
+
for indicator, (value, default) in indicators_map.items():
|
| 294 |
+
if pd.isna(value):
|
| 295 |
+
processed_values[indicator] = default
|
| 296 |
+
tech_indicators_status[indicator] = {
|
| 297 |
+
'value': default,
|
| 298 |
+
'is_real': False,
|
| 299 |
+
'source': 'default'
|
| 300 |
+
}
|
| 301 |
+
else:
|
| 302 |
+
processed_values[indicator] = value
|
| 303 |
+
tech_indicators_status[indicator] = {
|
| 304 |
+
'value': value,
|
| 305 |
+
'is_real': True,
|
| 306 |
+
'source': 'calculated'
|
| 307 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 308 |
|
| 309 |
+
# 建立簡化的特徵向量 (按您指定的順序)
|
| 310 |
features_list = [
|
| 311 |
+
latest_data['Close'], # close
|
| 312 |
+
sentiment_score_raw, # NEWS
|
| 313 |
+
processed_values['MACD'], # MACD
|
| 314 |
+
processed_values['MACDsign'], # MACDsign
|
| 315 |
+
processed_values['MACDvol'], # MACDvol
|
| 316 |
+
processed_values['K'], # K
|
| 317 |
+
processed_values['D'], # D
|
| 318 |
+
processed_values['+DI'], # +DI
|
| 319 |
+
processed_values['-DI'], # -DI
|
| 320 |
+
processed_values['ADX'], # ADX
|
| 321 |
+
processed_values['RSI'], # RSI
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 322 |
]
|
| 323 |
|
| 324 |
+
# 對應的欄位名稱
|
| 325 |
column_names = [
|
| 326 |
+
'close', 'NEWS', 'MACD', 'MACDsign', 'MACDvol',
|
| 327 |
+
'K', 'D', '+DI', '-DI', 'ADX', 'RSI'
|
|
|
|
| 328 |
]
|
| 329 |
|
| 330 |
+
# 轉換為 DataFrame (XGBoost 模型期望的格式)
|
| 331 |
input_df = pd.DataFrame([features_list], columns=column_names)
|
| 332 |
|
| 333 |
# 詳細的資料驗證日誌
|
| 334 |
print("=" * 50)
|
| 335 |
+
print("XGBoost 簡化模型輸入特徵檢查報告")
|
| 336 |
print("=" * 50)
|
| 337 |
|
| 338 |
+
# 基本資料
|
| 339 |
+
print(f"收盤價 (close): {latest_data['Close']:.2f}")
|
| 340 |
+
print(f"新聞情緒 (NEWS): {sentiment_score_raw:.6f}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 341 |
if sentiment_score_raw == 0:
|
| 342 |
print(" ⚠️ 新聞情緒分數為0,可能無新聞數據")
|
| 343 |
else:
|
|
|
|
| 345 |
|
| 346 |
# 技術指標詳細狀態
|
| 347 |
print("\n📈 技術指標狀態:")
|
| 348 |
+
for indicator in ['MACD', 'MACDsign', 'MACDvol', 'K', 'D', '+DI', '-DI', 'ADX', 'RSI']:
|
| 349 |
+
status = tech_indicators_status[indicator]
|
| 350 |
status_symbol = "✅" if status['is_real'] else "⚠️"
|
| 351 |
+
source_info = "實際計算" if status['is_real'] else "預設值"
|
| 352 |
+
print(f" {indicator:10s}: {status['value']:8.4f} {status_symbol} ({source_info})")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 353 |
|
| 354 |
+
# 統計完整性
|
| 355 |
real_indicators = sum(1 for status in tech_indicators_status.values() if status['is_real'])
|
| 356 |
total_indicators = len(tech_indicators_status)
|
| 357 |
completeness = (real_indicators / total_indicators) * 100
|
| 358 |
|
| 359 |
+
print(f"\n📋 技術指標完整性:")
|
| 360 |
print(f" 實際計算指標: {real_indicators}/{total_indicators} ({completeness:.1f}%)")
|
| 361 |
+
if completeness < 70:
|
| 362 |
+
print(" ⚠️ 警告:超過30%的指標使用預設值,可能影響預測準確性")
|
| 363 |
else:
|
| 364 |
+
print(" ✅ 指標完整性良好")
|
| 365 |
|
| 366 |
+
# 顯示完整特徵向量
|
| 367 |
print(f"\n🔢 完整特徵向量 (共{len(features_list)}個特徵):")
|
| 368 |
for i, (name, value) in enumerate(zip(column_names, features_list)):
|
| 369 |
+
print(f" [{i:2d}] {name:10s}: {value:12.6f}")
|
| 370 |
|
| 371 |
print("=" * 50)
|
| 372 |
|
|
|
|
| 387 |
pred_key = pred_mapping[closest_day]
|
| 388 |
|
| 389 |
predicted_price = predictions[pred_key]
|
| 390 |
+
current_price = latest_data['Close']
|
| 391 |
change_pct = ((predicted_price - current_price) / current_price) * 100
|
| 392 |
|
| 393 |
print(f"XGBoost 預測完成:")
|
|
|
|
| 395 |
print(f"- 當前價格: {current_price:.2f}")
|
| 396 |
print(f"- 預測價格: {predicted_price:.2f}")
|
| 397 |
print(f"- 預測變化: {change_pct:+.2f}%")
|
| 398 |
+
print(f"- 使用特徵數: {len(features_list)} 個")
|
| 399 |
|
| 400 |
return {
|
| 401 |
'predicted_price': predicted_price,
|
|
|
|
| 554 |
**你的任務:**
|
| 555 |
1. **基本面分析 (約 150 字):**
|
| 556 |
- 評論這家公司的產業地位、近期營運亮點或挑戰。
|
| 557 |
+
- 提及任何可能影響其基本面的關鍵因素 (例如:財報、法說會、政策、供應鏈變化等)。
|
| 558 |
- 請用專業、客觀的語氣撰寫。
|
| 559 |
|
| 560 |
2. **市場展望與投資建議 (約 150 字):**
|
| 561 |
- 基於上述所有資訊,提供對該股票的短期和中期市場展望。
|
| 562 |
+
- 提出具體的投資建議,例如:適合何種類型的投資人、潛在的風險點。
|
| 563 |
- 請直接提供分析內容,不要包含任何問候語。
|
| 564 |
|
| 565 |
**輸出格式:**
|