Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -217,59 +217,94 @@ def advanced_xgboost_predict(data, predict_days):
|
|
| 217 |
def advanced_xgboost_predict(data, predict_days):
|
| 218 |
"""
|
| 219 |
【進階模型橋接函式】
|
| 220 |
-
- 準備 XGBoost
|
| 221 |
-
-
|
| 222 |
- 將模型的輸出格式轉換為主程式所需的格式。
|
| 223 |
"""
|
| 224 |
if xgb_model is None or data.empty:
|
| 225 |
-
print("XGBoost 模型未載入或數據為空")
|
| 226 |
-
return None
|
| 227 |
-
|
| 228 |
-
# 確保數據有足夠的歷史記錄來計算技術指標
|
| 229 |
-
if len(data) < 30:
|
| 230 |
-
print("歷史數據不足,無法計算技術指標")
|
| 231 |
return None
|
| 232 |
|
| 233 |
try:
|
| 234 |
-
print(
|
| 235 |
|
| 236 |
-
#
|
| 237 |
-
|
|
|
|
|
|
|
| 238 |
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
5: 'Close_t5_pred',
|
| 243 |
-
10: 'Close_t10_pred',
|
| 244 |
-
20: 'Close_t20_pred'
|
| 245 |
-
}
|
| 246 |
|
| 247 |
-
|
|
|
|
|
|
|
| 248 |
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 257 |
|
| 258 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 259 |
current_price = data['Close'].iloc[-1]
|
| 260 |
change_pct = ((predicted_price - current_price) / current_price) * 100
|
| 261 |
|
| 262 |
-
|
| 263 |
-
'predicted_price':
|
| 264 |
-
'change_pct':
|
| 265 |
-
'confidence': 0.
|
| 266 |
}
|
| 267 |
-
|
| 268 |
-
print(f"XGBoost 預測成功: 當前價格={current_price:.2f}, 預測價格={predicted_price:.2f}, 變化={change_pct:.2f}%")
|
| 269 |
-
return result
|
| 270 |
-
|
| 271 |
except Exception as e:
|
| 272 |
-
print(f"執行 XGBoost
|
| 273 |
import traceback
|
| 274 |
traceback.print_exc()
|
| 275 |
return None
|
|
|
|
| 217 |
def advanced_xgboost_predict(data, predict_days):
|
| 218 |
"""
|
| 219 |
【進階模型橋接函式】
|
| 220 |
+
- 準備 XGBoost 模型所需的20個特徵。
|
| 221 |
+
- 呼叫模型进行预测。
|
| 222 |
- 將模型的輸出格式轉換為主程式所需的格式。
|
| 223 |
"""
|
| 224 |
if xgb_model is None or data.empty:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 225 |
return None
|
| 226 |
|
| 227 |
try:
|
| 228 |
+
print("正在準備 XGBoost 模型所需的20個輸入特徵...")
|
| 229 |
|
| 230 |
+
# 1. 獲取外部市場數據
|
| 231 |
+
# 建立一個與主股票數據相同索引的 DataFrame 以便對齊
|
| 232 |
+
start_date = data.index.min() - pd.Timedelta(days=5) # 提前幾天以確保數據填充
|
| 233 |
+
end_date = data.index.max()
|
| 234 |
|
| 235 |
+
df_aligned = pd.DataFrame(index=pd.date_range(start=start_date, end=end_date, freq='D'))
|
| 236 |
+
|
| 237 |
+
external_symbols = {'DJI': '^DJI', 'NAS': '^IXIC', 'SOX': '^SOX', 'S&P_500': '^GSPC', 'TSM_ADR': 'TSM'}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 238 |
|
| 239 |
+
# 使用 yf.download 一次性獲取所有外部數據
|
| 240 |
+
ext_data = yf.download(list(external_symbols.values()), start=start_date, end=end_date, progress=False)['Close']
|
| 241 |
+
ext_data.rename(columns={v: k for k, v in external_symbols.items()}, inplace=True)
|
| 242 |
|
| 243 |
+
# 2. 合併所有數據源
|
| 244 |
+
# 將主數據與外部數據合併
|
| 245 |
+
input_df = data.join(ext_data, how='left')
|
| 246 |
+
|
| 247 |
+
# 載入並合併景氣燈號和 PMI
|
| 248 |
+
df_climate = get_business_climate_data() #
|
| 249 |
+
df_pmi = get_pmi_data() #
|
| 250 |
+
|
| 251 |
+
# 將 'Date' 轉為 datetime 物件以進行合併
|
| 252 |
+
input_df.index = pd.to_datetime(input_df.index)
|
| 253 |
+
df_climate['Date'] = pd.to_datetime(df_climate['Date']) #
|
| 254 |
+
df_pmi['Date'] = pd.to_datetime(df_pmi['Date']) #
|
| 255 |
|
| 256 |
+
input_df = pd.merge(input_df, df_climate.rename(columns={'Index': 'business_climate'}), on='Date', how='left')
|
| 257 |
+
input_df = pd.merge(input_df, df_pmi.rename(columns={'Index': 'PMI'}), on='Date', how='left')
|
| 258 |
+
|
| 259 |
+
# 向前填充所有缺失值 (例如假日)
|
| 260 |
+
input_df.ffill(inplace=True)
|
| 261 |
+
input_df.bfill(inplace=True) # 向後填充開頭可能存在的缺失值
|
| 262 |
+
|
| 263 |
+
# 3. 計算技術指標與新增其他特徵
|
| 264 |
+
input_df = calculate_technical_indicators(input_df) #
|
| 265 |
+
news_score = predictor.get_news_index() if predictor else 0
|
| 266 |
+
input_df['NEWS'] = news_score if news_score is not None else 0
|
| 267 |
+
input_df['rate'] = 1.75 # 注意:此為利率佔位符,請替換為真實數據源
|
| 268 |
+
|
| 269 |
+
# 4. 格式化最終輸入
|
| 270 |
+
input_df.rename(columns={
|
| 271 |
+
'Close': 'close', 'Volume': 'volume', 'MACD_Signal': 'MACDsign',
|
| 272 |
+
'MACD_Histogram': 'MACDvol'
|
| 273 |
+
}, inplace=True)
|
| 274 |
+
|
| 275 |
+
columns_to_keep = [
|
| 276 |
+
'close', 'volume', 'rate', 'DJI', 'NAS', 'SOX', 'S&P_500', 'TSM_ADR', 'NEWS',
|
| 277 |
+
'RSI', 'MACD', 'MACDsign', 'MACDvol', 'K', 'D', '+DI', '-DI', 'ADX',
|
| 278 |
+
'business_climate', 'PMI'
|
| 279 |
+
]
|
| 280 |
+
|
| 281 |
+
# 確保所有欄位都存在
|
| 282 |
+
for col in columns_to_keep:
|
| 283 |
+
if col not in input_df.columns:
|
| 284 |
+
print(f"錯誤:準備好的資料中缺少欄位 '{col}'")
|
| 285 |
+
return None
|
| 286 |
+
|
| 287 |
+
final_input = input_df[columns_to_keep].tail(1)
|
| 288 |
+
|
| 289 |
+
if final_input.isnull().values.any():
|
| 290 |
+
print(f"警告: 最終輸入數據中存在缺失值,無法預測。\n{final_input.isnull().sum()}")
|
| 291 |
+
return None
|
| 292 |
+
|
| 293 |
+
# 5. 呼叫模型預測
|
| 294 |
+
print("特徵準備完成,呼叫 XGBoost 模型...")
|
| 295 |
+
predictions = xgb_model.predict('xgboost_model', final_input)
|
| 296 |
+
|
| 297 |
+
predicted_price = predictions['Close_t5_pred'] # 預設獲取5日預測
|
| 298 |
current_price = data['Close'].iloc[-1]
|
| 299 |
change_pct = ((predicted_price - current_price) / current_price) * 100
|
| 300 |
|
| 301 |
+
return {
|
| 302 |
+
'predicted_price': predicted_price,
|
| 303 |
+
'change_pct': change_pct,
|
| 304 |
+
'confidence': 0.95
|
| 305 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 306 |
except Exception as e:
|
| 307 |
+
print(f"執行 XGBoost 預測時發生嚴重錯誤: {e}")
|
| 308 |
import traceback
|
| 309 |
traceback.print_exc()
|
| 310 |
return None
|