Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -724,19 +724,26 @@ class RiskAnalyzer:
|
|
| 724 |
return covariance / market_variance if market_variance != 0 else 0
|
| 725 |
# ========================== 風險管理模組 END ==========================
|
| 726 |
|
| 727 |
-
def get_stock_data(symbol, period
|
| 728 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 729 |
try:
|
| 730 |
-
|
| 731 |
-
data =
|
| 732 |
-
if data.empty
|
| 733 |
-
|
| 734 |
-
|
| 735 |
-
if data.empty:
|
| 736 |
-
stock = yf.Ticker('^TWII')
|
| 737 |
-
data = stock.history(period=period)
|
| 738 |
return data
|
| 739 |
-
except:
|
|
|
|
| 740 |
return pd.DataFrame()
|
| 741 |
|
| 742 |
def get_us_market_data():
|
|
@@ -805,49 +812,71 @@ def simple_statistical_predict(data, predict_days=5):
|
|
| 805 |
'confidence': max(0.6, 1 - volatility * 2)
|
| 806 |
}
|
| 807 |
|
| 808 |
-
def
|
| 809 |
"""
|
| 810 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 811 |
"""
|
| 812 |
-
|
| 813 |
-
return df
|
| 814 |
|
| 815 |
-
# 1.
|
|
|
|
|
|
|
|
|
|
| 816 |
df['return_t-1'] = df['Close'].pct_change()
|
| 817 |
|
| 818 |
-
#
|
| 819 |
df['return_t-5'] = (df['Close'] / df['Close'].shift(5) - 1)
|
| 820 |
|
| 821 |
-
#
|
| 822 |
df['MA5_close'] = df['Close'].rolling(window=5).mean()
|
| 823 |
|
| 824 |
-
#
|
| 825 |
-
df['MA20_close'] = df['Close'].rolling(window=20).mean()
|
| 826 |
-
|
| 827 |
-
# 5. volatility_5d – 5 日報酬標準差(短期波動)
|
| 828 |
df['volatility_5d'] = df['return_t-1'].rolling(window=5).std()
|
| 829 |
|
| 830 |
-
# 6. volume_ratio_5d
|
| 831 |
df['volume_5d_avg'] = df['Volume'].rolling(window=5).mean()
|
| 832 |
df['volume_ratio_5d'] = df['Volume'] / df['volume_5d_avg']
|
| 833 |
|
| 834 |
-
# 7.
|
| 835 |
-
delta = df['Close'].diff()
|
| 836 |
-
gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
|
| 837 |
-
loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
|
| 838 |
-
rs = gain / loss
|
| 839 |
-
df['RSI_14'] = 100 - (100 / (1 + rs))
|
| 840 |
-
|
| 841 |
-
# 8. MACD_diff – MACD - signal(趨勢強弱)
|
| 842 |
exp1 = df['Close'].ewm(span=12).mean()
|
| 843 |
exp2 = df['Close'].ewm(span=26).mean()
|
| 844 |
macd_line = exp1 - exp2
|
| 845 |
signal_line = macd_line.ewm(span=9).mean()
|
| 846 |
df['MACD_diff'] = macd_line - signal_line
|
| 847 |
|
| 848 |
-
#
|
| 849 |
-
if
|
| 850 |
-
df =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 851 |
|
| 852 |
return df
|
| 853 |
|
|
@@ -1062,23 +1091,43 @@ def advanced_xgboost_predict(predict_days=5):
|
|
| 1062 |
traceback.print_exc()
|
| 1063 |
return None
|
| 1064 |
|
| 1065 |
-
def get_prediction(
|
| 1066 |
"""
|
| 1067 |
-
|
| 1068 |
-
根據 USE_ADVANCED_MODEL 的設定,呼叫對應的預測模型。
|
| 1069 |
-
"""
|
| 1070 |
-
if USE_ADVANCED_MODEL:
|
| 1071 |
-
print(f"模式: 進階XGBoost模型 | 預測天期: {predict_days}天")
|
| 1072 |
-
prediction = advanced_xgboost_predict(predict_days)
|
| 1073 |
-
# 如果進階模型預測失敗,則自動降級使用簡易模型
|
| 1074 |
-
if prediction is not None:
|
| 1075 |
-
return prediction
|
| 1076 |
-
else:
|
| 1077 |
-
print("進階模型預測失敗,自動降級為簡易統計模型。")
|
| 1078 |
|
| 1079 |
-
|
| 1080 |
-
|
| 1081 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1082 |
|
| 1083 |
def calculate_technical_indicators(df):
|
| 1084 |
"""計算技術指標"""
|
|
|
|
| 724 |
return covariance / market_variance if market_variance != 0 else 0
|
| 725 |
# ========================== 風險管理模組 END ==========================
|
| 726 |
|
| 727 |
+
def get_stock_data(symbol, period):
|
| 728 |
+
"""
|
| 729 |
+
從 yfinance 獲取股票數據
|
| 730 |
+
|
| 731 |
+
Args:
|
| 732 |
+
symbol: 股票代碼
|
| 733 |
+
period: 數據時間段(例如 '1mo', '3mo', '1y')
|
| 734 |
+
|
| 735 |
+
Returns:
|
| 736 |
+
pd.DataFrame: 股票數據
|
| 737 |
+
"""
|
| 738 |
try:
|
| 739 |
+
ticker = yf.Ticker(symbol)
|
| 740 |
+
data = ticker.history(period=period)
|
| 741 |
+
if data.empty:
|
| 742 |
+
print(f"警告:無法獲取 {symbol} 的數據")
|
| 743 |
+
return pd.DataFrame()
|
|
|
|
|
|
|
|
|
|
| 744 |
return data
|
| 745 |
+
except Exception as e:
|
| 746 |
+
print(f"獲取 {symbol} 數據時發生錯誤:{e}")
|
| 747 |
return pd.DataFrame()
|
| 748 |
|
| 749 |
def get_us_market_data():
|
|
|
|
| 812 |
'confidence': max(0.6, 1 - volatility * 2)
|
| 813 |
}
|
| 814 |
|
| 815 |
+
def calculate_features(stock_data, dji_data=None, sox_data=None, news_score=0.1):
|
| 816 |
"""
|
| 817 |
+
從股票數據計算 XGBoostModel 所需的特徵
|
| 818 |
+
|
| 819 |
+
Args:
|
| 820 |
+
stock_data: 來自 yfinance 的股票數據
|
| 821 |
+
dji_data: 道瓊指數數據
|
| 822 |
+
sox_data: 費半指數數據
|
| 823 |
+
news_score: 新聞情緒分數(預設 0.1)
|
| 824 |
+
|
| 825 |
+
Returns:
|
| 826 |
+
pd.DataFrame: 包含特徵的 DataFrame
|
| 827 |
"""
|
| 828 |
+
df = stock_data.copy()
|
|
|
|
| 829 |
|
| 830 |
+
# 1. close - 前一日收盤價
|
| 831 |
+
df['close'] = df['Close']
|
| 832 |
+
|
| 833 |
+
# 2. return_t-1 - 前一日報酬率
|
| 834 |
df['return_t-1'] = df['Close'].pct_change()
|
| 835 |
|
| 836 |
+
# 3. return_t-5 - 過去 5 日累積報酬率
|
| 837 |
df['return_t-5'] = (df['Close'] / df['Close'].shift(5) - 1)
|
| 838 |
|
| 839 |
+
# 4. MA5_close - 5 日移動平均價
|
| 840 |
df['MA5_close'] = df['Close'].rolling(window=5).mean()
|
| 841 |
|
| 842 |
+
# 5. volatility_5d - 5 日報酬標準差
|
|
|
|
|
|
|
|
|
|
| 843 |
df['volatility_5d'] = df['return_t-1'].rolling(window=5).std()
|
| 844 |
|
| 845 |
+
# 6. volume_ratio_5d - 今日成交量 ÷ 5 日均量
|
| 846 |
df['volume_5d_avg'] = df['Volume'].rolling(window=5).mean()
|
| 847 |
df['volume_ratio_5d'] = df['Volume'] / df['volume_5d_avg']
|
| 848 |
|
| 849 |
+
# 7. MACD_diff - MACD - signal
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 850 |
exp1 = df['Close'].ewm(span=12).mean()
|
| 851 |
exp2 = df['Close'].ewm(span=26).mean()
|
| 852 |
macd_line = exp1 - exp2
|
| 853 |
signal_line = macd_line.ewm(span=9).mean()
|
| 854 |
df['MACD_diff'] = macd_line - signal_line
|
| 855 |
|
| 856 |
+
# 8. dji_return_t-1 - 前一日道瓊指數報酬率
|
| 857 |
+
if dji_data is not None and not dji_data.empty:
|
| 858 |
+
df['dji_return_t-1'] = dji_data['Close'].pct_change()
|
| 859 |
+
else:
|
| 860 |
+
df['dji_return_t-1'] = 0.0
|
| 861 |
+
|
| 862 |
+
# 9. sox_return_t-1 - 前一日費半指數報酬率
|
| 863 |
+
if sox_data is not None and not sox_data.empty:
|
| 864 |
+
df['sox_return_t-1'] = sox_data['Close'].pct_change()
|
| 865 |
+
else:
|
| 866 |
+
df['sox_return_t-1'] = 0.0
|
| 867 |
+
|
| 868 |
+
# 10. NEWS - 新聞情緒分數
|
| 869 |
+
df['NEWS'] = news_score
|
| 870 |
+
|
| 871 |
+
# 選擇 XGBoostModel 所需的特徵
|
| 872 |
+
feature_columns = [
|
| 873 |
+
'close', 'return_t-1', 'return_t-5', 'MA5_close', 'volatility_5d',
|
| 874 |
+
'volume_ratio_5d', 'MACD_diff', 'dji_return_t-1', 'sox_return_t-1', 'NEWS'
|
| 875 |
+
]
|
| 876 |
+
df = df[feature_columns]
|
| 877 |
+
|
| 878 |
+
# 移除 NaN 值
|
| 879 |
+
df = df.fillna(method='ffill').fillna(0)
|
| 880 |
|
| 881 |
return df
|
| 882 |
|
|
|
|
| 1091 |
traceback.print_exc()
|
| 1092 |
return None
|
| 1093 |
|
| 1094 |
+
def get_prediction(stock_data, days):
|
| 1095 |
"""
|
| 1096 |
+
為給定的股票數據生成預測
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1097 |
|
| 1098 |
+
Args:
|
| 1099 |
+
stock_data: 股價歷史資料
|
| 1100 |
+
days: 預測天數 (1, 5, 10, 20)
|
| 1101 |
+
|
| 1102 |
+
Returns:
|
| 1103 |
+
float: 預測的漲幅百分比
|
| 1104 |
+
"""
|
| 1105 |
+
try:
|
| 1106 |
+
# 獲取道瓊和費半數據
|
| 1107 |
+
dji_data = get_stock_data('^DJI', '1mo')
|
| 1108 |
+
sox_data = get_stock_data('^SOX', '1mo')
|
| 1109 |
+
|
| 1110 |
+
# 計算特徵(使用最新一天數據)
|
| 1111 |
+
features_df = calculate_features(stock_data, dji_data, sox_data, news_score=0.1)
|
| 1112 |
+
if features_df.empty:
|
| 1113 |
+
return None
|
| 1114 |
+
|
| 1115 |
+
# 只取最新一天的特徵
|
| 1116 |
+
latest_features = features_df.tail(1)
|
| 1117 |
+
|
| 1118 |
+
# 初始化 XGBoost 模型
|
| 1119 |
+
model = XGBoostModel()
|
| 1120 |
+
|
| 1121 |
+
# 進行預測
|
| 1122 |
+
predictions = model.predict('xgboost_model', latest_features)
|
| 1123 |
+
if predictions is None:
|
| 1124 |
+
return None
|
| 1125 |
+
|
| 1126 |
+
target_key = f'Change_pct_t{days}_pred'
|
| 1127 |
+
return predictions.get(target_key, 0.0)
|
| 1128 |
+
except Exception as e:
|
| 1129 |
+
print(f"預測失敗:{e}")
|
| 1130 |
+
return None
|
| 1131 |
|
| 1132 |
def calculate_technical_indicators(df):
|
| 1133 |
"""計算技術指標"""
|