Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -933,112 +933,80 @@ def advanced_xgboost_predict(predict_days=5):
|
|
| 933 |
|
| 934 |
taiex_data = calculate_technical_indicators(taiex_data)
|
| 935 |
taiex_data = calculate_new_features(taiex_data)
|
|
|
|
|
|
|
| 936 |
|
| 937 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 938 |
print("正在獲取美股數據...")
|
| 939 |
us_market_data = get_us_market_data()
|
| 940 |
|
| 941 |
-
# 獲取新聞情緒分數
|
| 942 |
try:
|
| 943 |
if predictor is not None:
|
| 944 |
sentiment_score_raw = predictor.get_news_index()
|
| 945 |
-
if sentiment_score_raw is None:
|
| 946 |
-
sentiment_score_raw = 0
|
| 947 |
print(f"新聞情緒分數: {sentiment_score_raw}")
|
| 948 |
else:
|
| 949 |
sentiment_score_raw = 0
|
| 950 |
except:
|
| 951 |
sentiment_score_raw = 0
|
| 952 |
|
| 953 |
-
# 準備特徵數據
|
| 954 |
latest_data = taiex_data.iloc[-1]
|
| 955 |
-
|
| 956 |
-
|
| 957 |
-
|
| 958 |
-
|
| 959 |
-
'return_t-1',
|
| 960 |
-
'return_t-5',
|
| 961 |
-
'MA5_close',
|
| 962 |
-
'volatility_5d',
|
| 963 |
-
'volume_ratio_5d',
|
| 964 |
-
'MACD_diff',
|
| 965 |
-
'
|
| 966 |
-
'
|
| 967 |
-
'
|
| 968 |
-
'
|
| 969 |
-
|
| 970 |
-
|
| 971 |
-
|
| 972 |
-
# 獲取美股報酬率
|
| 973 |
dji_return = 0
|
| 974 |
sox_return = 0
|
| 975 |
-
|
| 976 |
try:
|
| 977 |
dji_data = get_stock_data('^DJI', '5d')
|
| 978 |
if not dji_data.empty and len(dji_data) >= 2:
|
| 979 |
dji_return = (dji_data['Close'].iloc[-1] / dji_data['Close'].iloc[-2] - 1)
|
| 980 |
-
|
| 981 |
-
except:
|
| 982 |
-
pass
|
| 983 |
-
|
| 984 |
try:
|
| 985 |
sox_data = get_stock_data('^SOX', '5d')
|
| 986 |
if not sox_data.empty and len(sox_data) >= 2:
|
| 987 |
sox_return = (sox_data['Close'].iloc[-1] / sox_data['Close'].iloc[-2] - 1)
|
| 988 |
-
|
| 989 |
-
except:
|
| 990 |
-
pass
|
| 991 |
-
|
| 992 |
-
# 建立特徵向量
|
| 993 |
-
features_list = []
|
| 994 |
-
feature_names = []
|
| 995 |
-
|
| 996 |
-
for feature in new_feature_columns:
|
| 997 |
-
if feature in latest_data.index:
|
| 998 |
-
value = latest_data[feature]
|
| 999 |
-
if pd.isna(value):
|
| 1000 |
-
if 'return' in feature: default_value = 0.0
|
| 1001 |
-
elif 'MA' in feature: default_value = latest_data['Close'] if not pd.isna(latest_data['Close']) else 100
|
| 1002 |
-
elif 'volatility' in feature: default_value = 0.02
|
| 1003 |
-
elif 'volume_ratio' in feature: default_value = 1.0
|
| 1004 |
-
elif 'MACD' in feature: default_value = 0.0
|
| 1005 |
-
else: default_value = 0.0
|
| 1006 |
-
|
| 1007 |
-
features_list.append(default_value)
|
| 1008 |
-
else:
|
| 1009 |
-
features_list.append(value)
|
| 1010 |
-
|
| 1011 |
-
feature_names.append(feature)
|
| 1012 |
|
| 1013 |
-
|
| 1014 |
-
|
| 1015 |
-
|
| 1016 |
-
|
| 1017 |
-
|
| 1018 |
-
|
|
|
|
|
|
|
|
|
|
| 1019 |
|
| 1020 |
-
print(f"特徵向量: {[f'{f:.4f}' for f in features_list[:5]]}...") # 只顯示前5個
|
| 1021 |
-
# 🔍 新增這段:完整印出本次預測輸入��料
|
| 1022 |
print("\n=== 📊 本次預測輸入特徵 DataFrame ===")
|
| 1023 |
print(input_df)
|
| 1024 |
-
print("
|
| 1025 |
|
| 1026 |
-
# 進行預測
|
| 1027 |
predictions = xgb_model.predict('xgboost_model', input_df)
|
| 1028 |
|
| 1029 |
if predictions is None:
|
| 1030 |
return None
|
| 1031 |
|
| 1032 |
-
|
| 1033 |
-
pred_mapping =
|
| 1034 |
-
1: 'Change_pct_t1_pred',
|
| 1035 |
-
5: 'Change_pct_t5_pred',
|
| 1036 |
-
10: 'Change_pct_t10_pred',
|
| 1037 |
-
20: 'Change_pct_t20_pred'
|
| 1038 |
-
}
|
| 1039 |
-
|
| 1040 |
-
available_days = [1, 5, 10, 20]
|
| 1041 |
-
closest_day = min(available_days, key=lambda x: abs(x - predict_days))
|
| 1042 |
pred_key = pred_mapping[closest_day]
|
| 1043 |
|
| 1044 |
predicted_change_pct = predictions[pred_key]
|
|
@@ -1047,11 +1015,7 @@ def advanced_xgboost_predict(predict_days=5):
|
|
| 1047 |
|
| 1048 |
print(f"XGBoost預測結果 - 漲幅: {predicted_change_pct:+.2f}%, 時間: {datetime.now().strftime('%H:%M:%S')}")
|
| 1049 |
|
| 1050 |
-
return {
|
| 1051 |
-
'predicted_price': predicted_price,
|
| 1052 |
-
'change_pct': predicted_change_pct,
|
| 1053 |
-
'confidence': 0.75
|
| 1054 |
-
}
|
| 1055 |
|
| 1056 |
except Exception as e:
|
| 1057 |
print(f"XGBoost 預測錯誤: {e}")
|
|
@@ -1146,7 +1110,15 @@ def calculate_technical_indicators(df):
|
|
| 1146 |
df['-DI'] = (df['-DM'].ewm(com=13, adjust=False).mean() / df['TR'].ewm(com=13, adjust=False).mean()) * 100
|
| 1147 |
df['DX'] = abs(df['+DI'] - df['-DI']) / (df['+DI'] + df['-DI']) * 100
|
| 1148 |
df['ADX'] = df['DX'].ewm(com=13, adjust=False).mean()
|
| 1149 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1150 |
return df
|
| 1151 |
|
| 1152 |
def calculate_volume_profile(df, num_bins=50):
|
|
|
|
| 933 |
|
| 934 |
taiex_data = calculate_technical_indicators(taiex_data)
|
| 935 |
taiex_data = calculate_new_features(taiex_data)
|
| 936 |
+
|
| 937 |
+
|
| 938 |
|
| 939 |
+
# 新增 volume_weighted_return 計算
|
| 940 |
+
if 'return_t-1' in taiex_data.columns and 'Volume' in taiex_data.columns:
|
| 941 |
+
taiex_data['volume_weighted_return'] = abs(taiex_data['return_t-1']) * taiex_data['Volume']
|
| 942 |
+
else:
|
| 943 |
+
taiex_data['volume_weighted_return'] = 0
|
| 944 |
+
|
| 945 |
print("正在獲取美股數據...")
|
| 946 |
us_market_data = get_us_market_data()
|
| 947 |
|
|
|
|
| 948 |
try:
|
| 949 |
if predictor is not None:
|
| 950 |
sentiment_score_raw = predictor.get_news_index()
|
| 951 |
+
if sentiment_score_raw is None: sentiment_score_raw = 0
|
|
|
|
| 952 |
print(f"新聞情緒分數: {sentiment_score_raw}")
|
| 953 |
else:
|
| 954 |
sentiment_score_raw = 0
|
| 955 |
except:
|
| 956 |
sentiment_score_raw = 0
|
| 957 |
|
|
|
|
| 958 |
latest_data = taiex_data.iloc[-1]
|
| 959 |
+
|
| 960 |
+
# 【【修改點】】定義完整的特徵列表
|
| 961 |
+
feature_columns_map = {
|
| 962 |
+
'close': latest_data.get('Close', 0),
|
| 963 |
+
'return_t-1': latest_data.get('return_t-1', 0),
|
| 964 |
+
'return_t-5': latest_data.get('return_t-5', 0),
|
| 965 |
+
'MA5_close': latest_data.get('MA5_close', 0),
|
| 966 |
+
'volatility_5d': latest_data.get('volatility_5d', 0),
|
| 967 |
+
'volume_ratio_5d': latest_data.get('volume_ratio_5d', 0),
|
| 968 |
+
'MACD_diff': latest_data.get('MACD_diff', 0),
|
| 969 |
+
'NEWS': sentiment_score_raw,
|
| 970 |
+
'MACDvol': latest_data.get('MACDvol', 0),
|
| 971 |
+
'RSI_14': latest_data.get('RSI', 0), # 注意: app.py中計算的欄位名為 'RSI'
|
| 972 |
+
'ADX': latest_data.get('ADX', 0),
|
| 973 |
+
'volume_weighted_return': latest_data.get('volume_weighted_return', 0)
|
| 974 |
+
}
|
| 975 |
+
|
|
|
|
| 976 |
dji_return = 0
|
| 977 |
sox_return = 0
|
|
|
|
| 978 |
try:
|
| 979 |
dji_data = get_stock_data('^DJI', '5d')
|
| 980 |
if not dji_data.empty and len(dji_data) >= 2:
|
| 981 |
dji_return = (dji_data['Close'].iloc[-1] / dji_data['Close'].iloc[-2] - 1)
|
| 982 |
+
except: pass
|
|
|
|
|
|
|
|
|
|
| 983 |
try:
|
| 984 |
sox_data = get_stock_data('^SOX', '5d')
|
| 985 |
if not sox_data.empty and len(sox_data) >= 2:
|
| 986 |
sox_return = (sox_data['Close'].iloc[-1] / sox_data['Close'].iloc[-2] - 1)
|
| 987 |
+
except: pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 988 |
|
| 989 |
+
feature_columns_map['dji_return_t-1'] = dji_return
|
| 990 |
+
feature_columns_map['sox_return_t-1'] = sox_return
|
| 991 |
+
|
| 992 |
+
# 處理可能的 NaN 值
|
| 993 |
+
for key, value in feature_columns_map.items():
|
| 994 |
+
if pd.isna(value):
|
| 995 |
+
feature_columns_map[key] = 0
|
| 996 |
+
|
| 997 |
+
input_df = pd.DataFrame([feature_columns_map])
|
| 998 |
|
|
|
|
|
|
|
| 999 |
print("\n=== 📊 本次預測輸入特徵 DataFrame ===")
|
| 1000 |
print(input_df)
|
| 1001 |
+
print("======================================\n")
|
| 1002 |
|
|
|
|
| 1003 |
predictions = xgb_model.predict('xgboost_model', input_df)
|
| 1004 |
|
| 1005 |
if predictions is None:
|
| 1006 |
return None
|
| 1007 |
|
| 1008 |
+
pred_mapping = {1: 'Change_pct_t1_pred', 5: 'Change_pct_t5_pred', 10: 'Change_pct_t10_pred', 20: 'Change_pct_t20_pred'}
|
| 1009 |
+
closest_day = min(pred_mapping.keys(), key=lambda x: abs(x - predict_days))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1010 |
pred_key = pred_mapping[closest_day]
|
| 1011 |
|
| 1012 |
predicted_change_pct = predictions[pred_key]
|
|
|
|
| 1015 |
|
| 1016 |
print(f"XGBoost預測結果 - 漲幅: {predicted_change_pct:+.2f}%, 時間: {datetime.now().strftime('%H:%M:%S')}")
|
| 1017 |
|
| 1018 |
+
return {'predicted_price': predicted_price, 'change_pct': predicted_change_pct, 'confidence': 0.75}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1019 |
|
| 1020 |
except Exception as e:
|
| 1021 |
print(f"XGBoost 預測錯誤: {e}")
|
|
|
|
| 1110 |
df['-DI'] = (df['-DM'].ewm(com=13, adjust=False).mean() / df['TR'].ewm(com=13, adjust=False).mean()) * 100
|
| 1111 |
df['DX'] = abs(df['+DI'] - df['-DI']) / (df['+DI'] + df['-DI']) * 100
|
| 1112 |
df['ADX'] = df['DX'].ewm(com=13, adjust=False).mean()
|
| 1113 |
+
|
| 1114 |
+
# 【【新增】】計算成交量MACD (MACDvol)
|
| 1115 |
+
if 'Volume' in df.columns and not df['Volume'].isnull().all():
|
| 1116 |
+
exp1_vol = df['Volume'].ewm(span=12, adjust=False).mean()
|
| 1117 |
+
exp2_vol = df['Volume'].ewm(span=26, adjust=False).mean()
|
| 1118 |
+
df['MACDvol'] = exp1_vol - exp2_vol
|
| 1119 |
+
else:
|
| 1120 |
+
df['MACDvol'] = 0 # 如果沒有成交量數據,則設為0
|
| 1121 |
+
|
| 1122 |
return df
|
| 1123 |
|
| 1124 |
def calculate_volume_profile(df, num_bins=50):
|