Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -530,10 +530,12 @@ USE_ADVANCED_MODEL = True
|
|
| 530 |
# ========================= CACHE 設定 START =========================
|
| 531 |
# 分析結果的快取字典
|
| 532 |
ANALYSIS_CACHE = {}
|
|
|
|
| 533 |
STOCK_DATA_CACHE = {}
|
| 534 |
CACHE_EXPIRE_SECONDS = 60 # 改為1分鐘,確保數據更及時
|
| 535 |
# 快取有效時間(秒),例如:8 小時 = 8 * 60 * 60 = 28800 秒
|
| 536 |
CACHE_DURATION_SECONDS = 60 # 60秒緩存
|
|
|
|
| 537 |
# ========================== CACHE 設定 END ==========================
|
| 538 |
# ========================== 全域設定 END ==========================
|
| 539 |
|
|
@@ -725,27 +727,19 @@ class RiskAnalyzer:
|
|
| 725 |
market_variance = np.var(combined['market'])
|
| 726 |
return covariance / market_variance if market_variance != 0 else 0
|
| 727 |
# ========================== 風險管理模組 END ==========================
|
| 728 |
-
def
|
| 729 |
-
"""
|
| 730 |
-
global
|
| 731 |
-
|
| 732 |
-
# 清理所有緩存
|
| 733 |
-
STOCK_DATA_CACHE.clear()
|
| 734 |
ANALYSIS_CACHE.clear()
|
| 735 |
if 'BACKTEST_CACHE' in globals():
|
| 736 |
BACKTEST_CACHE.clear()
|
| 737 |
-
|
| 738 |
-
print("應用初始化完成 - 緩存已清空")
|
| 739 |
-
print(f"股票數據緩存時間: {CACHE_EXPIRE_SECONDS}秒")
|
| 740 |
-
print(f"分析結果緩存時間: {ANALYSIS_CACHE_DURATION}秒")
|
| 741 |
|
| 742 |
def get_stock_data(symbol, period='1y'):
|
| 743 |
-
"""獲取股票資料 -
|
| 744 |
try:
|
| 745 |
-
current_time =
|
| 746 |
-
|
| 747 |
-
|
| 748 |
-
print(f"正在獲取股票數據: {symbol}, 時間: {datetime.now().strftime('%H:%M:%S')}")
|
| 749 |
|
| 750 |
stock = yf.Ticker(symbol)
|
| 751 |
data = stock.history(period=period)
|
|
@@ -758,11 +752,15 @@ def get_stock_data(symbol, period='1y'):
|
|
| 758 |
data = stock.history(period=period)
|
| 759 |
|
| 760 |
if not data.empty:
|
| 761 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 762 |
|
| 763 |
return data
|
| 764 |
except Exception as e:
|
| 765 |
-
print(f"
|
| 766 |
return pd.DataFrame()
|
| 767 |
|
| 768 |
def get_us_market_data():
|
|
@@ -804,7 +802,7 @@ def get_exchange_rate():
|
|
| 804 |
return 31.5
|
| 805 |
|
| 806 |
def simple_statistical_predict(data, predict_days=5):
|
| 807 |
-
"""簡化的統計預測模型 -
|
| 808 |
if len(data) < 60:
|
| 809 |
return None
|
| 810 |
|
|
@@ -817,21 +815,28 @@ def simple_statistical_predict(data, predict_days=5):
|
|
| 817 |
recent_trend = np.polyfit(range(20), prices[-20:], 1)[0]
|
| 818 |
volatility = np.std(prices[-20:]) / np.mean(prices[-20:])
|
| 819 |
|
| 820 |
-
#
|
| 821 |
-
|
| 822 |
-
current_minute = datetime.now().minute
|
| 823 |
|
| 824 |
-
#
|
| 825 |
-
|
| 826 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 827 |
else:
|
| 828 |
-
|
| 829 |
|
| 830 |
base_change = recent_trend * predict_days
|
| 831 |
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)
|
| 832 |
|
| 833 |
-
#
|
| 834 |
-
|
|
|
|
| 835 |
|
| 836 |
# 計算漲幅百分比
|
| 837 |
change_pct = ((predicted_price - current_price) / current_price) * 100
|
|
@@ -839,7 +844,7 @@ def simple_statistical_predict(data, predict_days=5):
|
|
| 839 |
|
| 840 |
confidence = max(0.6, 1 - volatility * 2)
|
| 841 |
|
| 842 |
-
print(f"統計預測 - 時間因子: {
|
| 843 |
|
| 844 |
return {
|
| 845 |
'predicted_price': predicted_price,
|
|
@@ -1774,23 +1779,25 @@ def update_business_climate_chart(selected_stock):
|
|
| 1774 |
Input('period-dropdown', 'value')]
|
| 1775 |
)
|
| 1776 |
def update_analysis_text(selected_stock, period):
|
| 1777 |
-
"""修改版分析文本更新"""
|
| 1778 |
-
#
|
| 1779 |
-
cache_duration = 60
|
| 1780 |
cache_key = f"{selected_stock}-{period}"
|
| 1781 |
current_time = time.time()
|
| 1782 |
|
| 1783 |
-
#
|
| 1784 |
if cache_key in ANALYSIS_CACHE:
|
| 1785 |
cached_data = ANALYSIS_CACHE[cache_key]
|
| 1786 |
-
|
| 1787 |
-
|
|
|
|
| 1788 |
return cached_data['technical'], cached_data['fundamental'], cached_data['outlook']
|
| 1789 |
|
| 1790 |
-
print(f"重新生成分析: {cache_key}
|
| 1791 |
|
|
|
|
| 1792 |
data = get_stock_data(selected_stock, period)
|
| 1793 |
stock_name = [name for name, symbol in TAIWAN_STOCKS.items() if symbol == selected_stock][0]
|
|
|
|
| 1794 |
if data.empty or len(data) < 20:
|
| 1795 |
return "資料不足,無法分析", "資料不足,無法分析", "資料不足,無法分析"
|
| 1796 |
|
|
@@ -1802,20 +1809,32 @@ def update_analysis_text(selected_stock, period):
|
|
| 1802 |
macd_current = data['MACD'].iloc[-1] if not pd.isna(data['MACD'].iloc[-1]) else 0
|
| 1803 |
macd_signal_current = data['MACD_Signal'].iloc[-1] if not pd.isna(data['MACD_Signal'].iloc[-1]) else 0
|
| 1804 |
|
|
|
|
|
|
|
| 1805 |
latest_date = data.index[-1].strftime('%Y-%m-%d')
|
| 1806 |
-
current_time_str = datetime.now().strftime('%H:%M:%S')
|
| 1807 |
|
| 1808 |
technical_text = html.Div([
|
| 1809 |
-
html.P([html.Strong("
|
| 1810 |
-
html.P([html.Strong("價格趨勢:"), f"在最近 {period} 期間內,{stock_name} 股價呈現",
|
| 1811 |
-
|
| 1812 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1813 |
])
|
| 1814 |
|
| 1815 |
-
#
|
| 1816 |
fundamental_text, market_outlook_text = generate_gemini_analysis(stock_name, selected_stock, period, data)
|
| 1817 |
|
| 1818 |
-
#
|
| 1819 |
ANALYSIS_CACHE[cache_key] = {
|
| 1820 |
'technical': technical_text,
|
| 1821 |
'fundamental': fundamental_text,
|
|
@@ -2284,6 +2303,11 @@ def create_trading_details_table(trades_df):
|
|
| 2284 |
|
| 2285 |
# 主程式執行
|
| 2286 |
if __name__ == '__main__':
|
| 2287 |
-
|
| 2288 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2289 |
app.run(host="0.0.0.0", port=7860, debug=False)
|
|
|
|
| 530 |
# ========================= CACHE 設定 START =========================
|
| 531 |
# 分析結果的快取字典
|
| 532 |
ANALYSIS_CACHE = {}
|
| 533 |
+
ANALYSIS_CACHE_DURATION = 60 # 分析結果緩存60秒
|
| 534 |
STOCK_DATA_CACHE = {}
|
| 535 |
CACHE_EXPIRE_SECONDS = 60 # 改為1分鐘,確保數據更及時
|
| 536 |
# 快取有效時間(秒),例如:8 小時 = 8 * 60 * 60 = 28800 秒
|
| 537 |
CACHE_DURATION_SECONDS = 60 # 60秒緩存
|
| 538 |
+
|
| 539 |
# ========================== CACHE 設定 END ==========================
|
| 540 |
# ========================== 全域設定 END ==========================
|
| 541 |
|
|
|
|
| 727 |
market_variance = np.var(combined['market'])
|
| 728 |
return covariance / market_variance if market_variance != 0 else 0
|
| 729 |
# ========================== 風險管理模組 END ==========================
|
| 730 |
+
def clear_all_cache():
|
| 731 |
+
"""清理所有緩存"""
|
| 732 |
+
global ANALYSIS_CACHE, BACKTEST_CACHE
|
|
|
|
|
|
|
|
|
|
| 733 |
ANALYSIS_CACHE.clear()
|
| 734 |
if 'BACKTEST_CACHE' in globals():
|
| 735 |
BACKTEST_CACHE.clear()
|
| 736 |
+
print("所有緩存已清空")
|
|
|
|
|
|
|
|
|
|
| 737 |
|
| 738 |
def get_stock_data(symbol, period='1y'):
|
| 739 |
+
"""獲取股票資料 - 添加調試信息"""
|
| 740 |
try:
|
| 741 |
+
current_time = datetime.now()
|
| 742 |
+
print(f"[{current_time.strftime('%H:%M:%S')}] 正在獲取 {symbol} 數據...")
|
|
|
|
|
|
|
| 743 |
|
| 744 |
stock = yf.Ticker(symbol)
|
| 745 |
data = stock.history(period=period)
|
|
|
|
| 752 |
data = stock.history(period=period)
|
| 753 |
|
| 754 |
if not data.empty:
|
| 755 |
+
latest_close = data['Close'].iloc[-1]
|
| 756 |
+
latest_date = data.index[-1].strftime('%Y-%m-%d')
|
| 757 |
+
print(f"[{current_time.strftime('%H:%M:%S')}] {symbol} 數據獲取成功: {latest_date}, 收盤價: {latest_close:.2f}")
|
| 758 |
+
else:
|
| 759 |
+
print(f"[{current_time.strftime('%H:%M:%S')}] 警告: {symbol} 數據為空")
|
| 760 |
|
| 761 |
return data
|
| 762 |
except Exception as e:
|
| 763 |
+
print(f"獲取 {symbol} 數據時發生錯誤: {e}")
|
| 764 |
return pd.DataFrame()
|
| 765 |
|
| 766 |
def get_us_market_data():
|
|
|
|
| 802 |
return 31.5
|
| 803 |
|
| 804 |
def simple_statistical_predict(data, predict_days=5):
|
| 805 |
+
"""簡化的統計預測模型 - 基於真實市場時間變化"""
|
| 806 |
if len(data) < 60:
|
| 807 |
return None
|
| 808 |
|
|
|
|
| 815 |
recent_trend = np.polyfit(range(20), prices[-20:], 1)[0]
|
| 816 |
volatility = np.std(prices[-20:]) / np.mean(prices[-20:])
|
| 817 |
|
| 818 |
+
# 使用系統時間作為市場變化因子
|
| 819 |
+
now = datetime.now()
|
|
|
|
| 820 |
|
| 821 |
+
# 時間相關因子(基於當前時間)
|
| 822 |
+
hour_factor = (now.hour % 24) * 0.0001 # 小時因子
|
| 823 |
+
minute_factor = now.minute * 0.000001 # 分鐘因子
|
| 824 |
+
second_factor = now.second * 0.0000001 # 秒因子
|
| 825 |
+
|
| 826 |
+
time_factor = hour_factor + minute_factor + second_factor
|
| 827 |
+
|
| 828 |
+
# 市場開盤時間調整
|
| 829 |
+
if 9 <= now.hour <= 13: # 台股交易時間
|
| 830 |
+
market_active_factor = 0.001
|
| 831 |
else:
|
| 832 |
+
market_active_factor = -0.0005
|
| 833 |
|
| 834 |
base_change = recent_trend * predict_days
|
| 835 |
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)
|
| 836 |
|
| 837 |
+
# 結合時間因子
|
| 838 |
+
total_time_factor = time_factor + market_active_factor
|
| 839 |
+
predicted_price = current_price * trend_factor + base_change + (current_price * total_time_factor)
|
| 840 |
|
| 841 |
# 計算漲幅百分比
|
| 842 |
change_pct = ((predicted_price - current_price) / current_price) * 100
|
|
|
|
| 844 |
|
| 845 |
confidence = max(0.6, 1 - volatility * 2)
|
| 846 |
|
| 847 |
+
print(f"[{now.strftime('%H:%M:%S')}] 統計預測 - 時間因子: {total_time_factor:.8f}, 預測漲幅: {change_pct:+.2f}%")
|
| 848 |
|
| 849 |
return {
|
| 850 |
'predicted_price': predicted_price,
|
|
|
|
| 1779 |
Input('period-dropdown', 'value')]
|
| 1780 |
)
|
| 1781 |
def update_analysis_text(selected_stock, period):
|
| 1782 |
+
"""修改版分析文本更新 - 使用正確的變數名稱"""
|
| 1783 |
+
# 建立快取的唯一鍵值
|
|
|
|
| 1784 |
cache_key = f"{selected_stock}-{period}"
|
| 1785 |
current_time = time.time()
|
| 1786 |
|
| 1787 |
+
# 檢查快取 - 使用正確定義的變數
|
| 1788 |
if cache_key in ANALYSIS_CACHE:
|
| 1789 |
cached_data = ANALYSIS_CACHE[cache_key]
|
| 1790 |
+
# 使用全域變數 ANALYSIS_CACHE_DURATION
|
| 1791 |
+
if current_time - cached_data['timestamp'] < ANALYSIS_CACHE_DURATION:
|
| 1792 |
+
print(f"從快取載入分析: {cache_key}")
|
| 1793 |
return cached_data['technical'], cached_data['fundamental'], cached_data['outlook']
|
| 1794 |
|
| 1795 |
+
print(f"重新生成分析: {cache_key}")
|
| 1796 |
|
| 1797 |
+
# 獲取股票數據
|
| 1798 |
data = get_stock_data(selected_stock, period)
|
| 1799 |
stock_name = [name for name, symbol in TAIWAN_STOCKS.items() if symbol == selected_stock][0]
|
| 1800 |
+
|
| 1801 |
if data.empty or len(data) < 20:
|
| 1802 |
return "資料不足,無法分析", "資料不足,無法分析", "資料不足,無法分析"
|
| 1803 |
|
|
|
|
| 1809 |
macd_current = data['MACD'].iloc[-1] if not pd.isna(data['MACD'].iloc[-1]) else 0
|
| 1810 |
macd_signal_current = data['MACD_Signal'].iloc[-1] if not pd.isna(data['MACD_Signal'].iloc[-1]) else 0
|
| 1811 |
|
| 1812 |
+
# 添加時間戳顯示數據更新時間
|
| 1813 |
+
update_time = datetime.now().strftime('%H:%M:%S')
|
| 1814 |
latest_date = data.index[-1].strftime('%Y-%m-%d')
|
|
|
|
| 1815 |
|
| 1816 |
technical_text = html.Div([
|
| 1817 |
+
html.P([html.Strong("數據更新時間:"), f"{latest_date} {update_time}"]),
|
| 1818 |
+
html.P([html.Strong("價格趨勢:"), f"在最近 {period} 期間內,{stock_name} 股價呈現",
|
| 1819 |
+
html.Span(f"{'上漲' if price_change > 5 else '下跌' if price_change < -5 else '盤整'}",
|
| 1820 |
+
style={'color': 'red' if price_change > 5 else 'green' if price_change < -5 else 'orange',
|
| 1821 |
+
'font-weight': 'bold'}),
|
| 1822 |
+
f"走勢,累計變動 {price_change:+.1f}%。"]),
|
| 1823 |
+
html.P([html.Strong("RSI 指標:"), f"目前的 RSI 值為 {rsi_current:.1f},",
|
| 1824 |
+
html.Span("處於超買區(>70)" if rsi_current > 70 else "處於超賣區(<30)" if rsi_current < 30 else "在正常範圍內",
|
| 1825 |
+
style={'color': 'green' if rsi_current > 70 else 'red' if rsi_current < 30 else 'blue',
|
| 1826 |
+
'font-weight': 'bold'}), "。"]),
|
| 1827 |
+
html.P([html.Strong("MACD 指標:"), f"MACD 快線 ({macd_current:.3f}) 目前",
|
| 1828 |
+
html.Span("高於" if macd_current > macd_signal_current else "低於",
|
| 1829 |
+
style={'color': 'red' if macd_current > macd_signal_current else 'green',
|
| 1830 |
+
'font-weight': 'bold'}),
|
| 1831 |
+
f" Signal 慢線 ({macd_signal_current:.3f})。"]),
|
| 1832 |
])
|
| 1833 |
|
| 1834 |
+
# 基本面與展望分析 (調用 Gemini)
|
| 1835 |
fundamental_text, market_outlook_text = generate_gemini_analysis(stock_name, selected_stock, period, data)
|
| 1836 |
|
| 1837 |
+
# 將新產生的結果��入快取
|
| 1838 |
ANALYSIS_CACHE[cache_key] = {
|
| 1839 |
'technical': technical_text,
|
| 1840 |
'fundamental': fundamental_text,
|
|
|
|
| 2303 |
|
| 2304 |
# 主程式執行
|
| 2305 |
if __name__ == '__main__':
|
| 2306 |
+
print("=== 應用啟動 ===")
|
| 2307 |
+
print(f"分析緩存時間: {ANALYSIS_CACHE_DURATION}秒")
|
| 2308 |
+
print(f"股票數據緩存時間: {CACHE_DURATION_SECONDS}秒")
|
| 2309 |
+
|
| 2310 |
+
# 清理緩存確保乾淨啟動
|
| 2311 |
+
clear_all_cache()
|
| 2312 |
+
|
| 2313 |
app.run(host="0.0.0.0", port=7860, debug=False)
|