# 由 Copilot 生成 - AI 股票分析師 (含批次分析功能)
import subprocess
import sys
import os
from datetime import datetime
# 環境檢測
IS_HUGGINGFACE_SPACE = "SPACE_ID" in os.environ
print(f"運行環境: {'Hugging Face Spaces' if IS_HUGGINGFACE_SPACE else '本地環境'}")
# 檢查並安裝所需套件的函數
def install_package(package_name):
try:
__import__(package_name)
except ImportError:
print(f"正在安裝 {package_name}...")
subprocess.check_call([sys.executable, "-m", "pip", "install", package_name])
# 安裝必要套件
required_packages = [
"torch>=2.0.0",
"torchvision>=0.15.0",
"torchaudio>=2.0.0",
"yfinance>=0.2.18",
"gradio>=4.0.0",
"pandas>=1.5.0",
"numpy>=1.21.0",
"matplotlib>=3.5.0",
"plotly>=5.0.0",
"beautifulsoup4>=4.11.0",
"requests>=2.28.0",
"transformers>=4.21.0",
"accelerate>=0.20.0",
"tokenizers>=0.13.0"
]
for package in required_packages:
package_name = package.split(">=")[0].split("==")[0]
if package_name == "beautifulsoup4":
package_name = "bs4"
try:
__import__(package_name)
except ImportError:
print(f"正在安裝 {package}...")
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
# 現在導入所有套件
import gradio as gr
import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import plotly.graph_objects as go
import plotly.express as px
from datetime import datetime, timedelta
import requests
from bs4 import BeautifulSoup
from transformers import pipeline
import warnings
warnings.filterwarnings('ignore')
# 初始化 Hugging Face 模型
print("正在載入 AI 模型...")
# 嘗試載入模型,如果失敗則使用較輕量的替代方案
try:
sentiment_analyzer = pipeline("sentiment-analysis", model="ProsusAI/finbert")
print("FinBERT 情感分析模型載入成功")
except Exception as e:
print(f"FinBERT 載入失敗,嘗試替代模型: {e}")
try:
sentiment_analyzer = pipeline("sentiment-analysis", model="nlptown/bert-base-multilingual-uncased-sentiment")
print("多語言情感分析模型載入成功")
except Exception as e2:
print(f"替代模型載入失敗: {e2}")
sentiment_analyzer = None
try:
summarizer = pipeline("summarization", model="facebook/bart-large-cnn")
print("BART 摘要模型載入成功")
except Exception as e:
print(f"BART 載入失敗,嘗試替代模型: {e}")
try:
summarizer = pipeline("summarization", model="sshleifer/distilbart-cnn-12-6")
print("DistilBART 摘要模型載入成功")
except Exception as e2:
print(f"摘要模型載入失敗: {e2}")
summarizer = None
class StockAnalyzer:
def __init__(self):
self.data = None
self.symbol = None
def fetch_stock_data(self, symbol, period="1y"):
"""獲取股票歷史數據"""
try:
ticker = yf.Ticker(symbol)
self.data = ticker.history(period=period)
self.symbol = symbol
# 獲取股票資訊
info = ticker.info
stock_name = info.get('longName', info.get('shortName', symbol))
return True, f"成功獲取 {symbol} 的歷史數據", stock_name
except Exception as e:
return False, f"數據獲取失敗: {str(e)}", None
def get_stock_info(self, symbol):
"""獲取股票基本資訊"""
try:
ticker = yf.Ticker(symbol)
info = ticker.info
current_price = self.data['Close'].iloc[-1] if self.data is not None else None
stock_name = info.get('longName', info.get('shortName', symbol))
return {
'name': stock_name,
'current_price': current_price,
'symbol': symbol
}
except Exception as e:
return {
'name': symbol,
'current_price': None,
'symbol': symbol
}
def calculate_technical_indicators(self):
"""計算技術指標"""
if self.data is None:
return None
df = self.data.copy()
# 移動平均線
df['MA5'] = df['Close'].rolling(window=5).mean()
df['MA10'] = df['Close'].rolling(window=10).mean()
df['MA20'] = df['Close'].rolling(window=20).mean()
df['MA60'] = df['Close'].rolling(window=60).mean()
# RSI 相對強弱指標
delta = df['Close'].diff()
gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
rs = gain / loss
df['RSI'] = 100 - (100 / (1 + rs))
# MACD
exp1 = df['Close'].ewm(span=12).mean()
exp2 = df['Close'].ewm(span=26).mean()
df['MACD'] = exp1 - exp2
df['MACD_signal'] = df['MACD'].ewm(span=9).mean()
# 布林通道
df['BB_middle'] = df['Close'].rolling(window=20).mean()
bb_std = df['Close'].rolling(window=20).std()
df['BB_upper'] = df['BB_middle'] + (bb_std * 2)
df['BB_lower'] = df['BB_middle'] - (bb_std * 2)
return df
def get_news_sentiment(self, symbol):
"""獲取並分析新聞情感"""
try:
# 模擬新聞標題(實際應用中需要接入新聞 API)
sample_news = [
f"{symbol} 股價創新高,投資人信心大增",
f"市場關注 {symbol} 最新財報表現",
f"{symbol} 面臨供應鏈挑戰,股價承壓",
f"分析師上調 {symbol} 目標價,看好後市",
f"{symbol} 技術創新獲得市場認可"
]
sentiments = []
# 檢查情感分析模型是否可用
if sentiment_analyzer is None:
# 如果模型不可用,返回模擬的情感分析結果
for news in sample_news:
# 簡單的關鍵詞情感分析替代方案
positive_words = ['創新高', '信心大增', '上調', '看好', '創新', '獲得認可']
negative_words = ['挑戰', '承壓', '面臨', '下滑']
score = 0.5 # 中性
sentiment = 'NEUTRAL'
for word in positive_words:
if word in news:
score = 0.8
sentiment = 'POSITIVE'
break
for word in negative_words:
if word in news:
score = 0.8
sentiment = 'NEGATIVE'
break
sentiments.append({
'text': news,
'sentiment': sentiment,
'score': score
})
else:
# 使用 AI 模型進行情感分析
for news in sample_news:
result = sentiment_analyzer(news)[0]
sentiments.append({
'text': news,
'sentiment': result['label'],
'score': result['score']
})
return sentiments
except Exception as e:
return [{'text': f'新聞分析暫時無法使用: {str(e)}', 'sentiment': 'NEUTRAL', 'score': 0.5}]
def analyze_sentiment_summary(self, sentiments):
"""分析情感摘要"""
if not sentiments:
return "中性"
positive_count = sum(1 for s in sentiments if s['sentiment'] == 'POSITIVE')
negative_count = sum(1 for s in sentiments if s['sentiment'] == 'NEGATIVE')
if positive_count > negative_count:
return "偏樂觀"
elif negative_count > positive_count:
return "偏悲觀"
else:
return "中性"
def calculate_prediction_probabilities(self, technical_signals, sentiment, recent_data):
"""計算上漲和下跌機率"""
# 計算技術面得分
bullish_signals = sum(1 for signal in technical_signals if "多頭" in signal or "機會" in signal)
bearish_signals = sum(1 for signal in technical_signals if "空頭" in signal or "警訊" in signal)
neutral_signals = len(technical_signals) - bullish_signals - bearish_signals
# 技術面得分 (-1 到 1)
total_signals = len(technical_signals)
if total_signals > 0:
tech_score = (bullish_signals - bearish_signals) / total_signals
else:
tech_score = 0
# 情感得分 (-1 到 1)
sentiment_score = 0
if sentiment == "偏樂觀":
sentiment_score = 0.6
elif sentiment == "偏悲觀":
sentiment_score = -0.6
else:
sentiment_score = 0
# 價格動量得分
price_change = ((recent_data['Close'].iloc[-1] - recent_data['Close'].iloc[-5]) / recent_data['Close'].iloc[-5]) * 100
momentum_score = np.tanh(price_change / 10) # 標準化到 -1 到 1
# RSI 得分
latest = recent_data.iloc[-1]
rsi = latest.get('RSI', 50)
if rsi > 70:
rsi_score = -0.5 # 超買,偏空
elif rsi < 30:
rsi_score = 0.5 # 超賣,偏多
else:
rsi_score = (50 - rsi) / 100 # 標準化
# MACD 得分
macd_score = 0
if 'MACD' in latest and 'MACD_signal' in latest:
if latest['MACD'] > latest['MACD_signal']:
macd_score = 0.3
else:
macd_score = -0.3
# 綜合得分計算(加權平均)
weights = {
'tech': 0.25,
'sentiment': 0.20,
'momentum': 0.25,
'rsi': 0.15,
'macd': 0.15
}
total_score = (
tech_score * weights['tech'] +
sentiment_score * weights['sentiment'] +
momentum_score * weights['momentum'] +
rsi_score * weights['rsi'] +
macd_score * weights['macd']
)
# 將得分轉換為機率 (使用 sigmoid 函數)
def sigmoid(x):
return 1 / (1 + np.exp(-x * 3)) # 放大 3 倍讓機率更明顯
up_probability = sigmoid(total_score) * 100
down_probability = sigmoid(-total_score) * 100
sideways_probability = 100 - up_probability - down_probability
# 確保機率總和為 100%
total_prob = up_probability + down_probability + sideways_probability
up_probability = (up_probability / total_prob) * 100
down_probability = (down_probability / total_prob) * 100
sideways_probability = (sideways_probability / total_prob) * 100
return {
'up': max(15, min(75, up_probability)), # 限制在 15%-75% 範圍內
'down': max(15, min(75, down_probability)), # 限制在 15%-75% 範圍內
'sideways': max(10, sideways_probability), # 至少 10%
'confidence': abs(total_score) # 信心度
}
def generate_buy_sell_recommendation(self, probabilities, confidence, recent_data):
"""生成具體的買賣建議 - 由 Copilot 生成"""
up_prob = probabilities['up']
down_prob = probabilities['down']
sideways_prob = probabilities['sideways']
# 當前價格和近期波動
current_price = recent_data['Close'].iloc[-1]
rsi = recent_data.iloc[-1].get('RSI', 50)
# 計算綜合評分
composite_score = up_prob * confidence
recommendations = {
'holding_advice': "", # 持有股票時的建議
'non_holding_advice': "", # 未持有時的建議
'risk_level': "", # 風險等級
'action_priority': "", # 操作優先級
'position_sizing': "" # 倉位建議
}
# 根據機率和信心度決定建議
if up_prob >= 60 and confidence >= 0.3:
# 強烈看多
recommendations['holding_advice'] = "🔥 **強烈建議持有**:股價有很高機率上漲,建議繼續持有並可考慮加碼。設定止損點在當前價格下方5-8%。"
recommendations['non_holding_advice'] = "🚀 **積極買進**:建議分批進場,可先買入30-50%預期倉位,若回檔至支撐位再加碼。"
recommendations['risk_level'] = "中等風險"
recommendations['action_priority'] = "高優先級 - 建議行動"
recommendations['position_sizing'] = "建議倉位:60-80%"
elif up_prob >= 45 and up_prob < 60 and confidence >= 0.25:
# 溫和看多
recommendations['holding_advice'] = "✅ **建議持有**:維持現有持股,若股價突破關鍵阻力位可考慮小幅加碼。設定止損在-8%。"
recommendations['non_holding_advice'] = "💰 **適度買進**:可小量進場試單,建議先買入20-30%預期倉位,觀察後續走勢。"
recommendations['risk_level'] = "中等風險"
recommendations['action_priority'] = "中優先級 - 可考慮行動"
recommendations['position_sizing'] = "建議倉位:40-60%"
elif down_prob >= 60 and confidence >= 0.3:
# 強烈看空
recommendations['holding_advice'] = "🚨 **建議賣出**:股價下跌機率很高,建議減持50-80%持股,保留核心持股並嚴格設置止損。"
recommendations['non_holding_advice'] = "⛔ **強烈不建議買進**:市場風險較大,建議等待更佳進場時機,保持現金或考慮避險資產。"
recommendations['risk_level'] = "高風險"
recommendations['action_priority'] = "高優先級 - 建議防守"
recommendations['position_sizing'] = "建議倉位:10-20%"
elif down_prob >= 45 and down_prob < 60 and confidence >= 0.25:
# 溫和看空
recommendations['holding_advice'] = "⚠️ **謹慎持有**:可減持部分持股降低風險,保留核心持股,密切關注支撐位是否守住。"
recommendations['non_holding_advice'] = "🔍 **暫緩買進**:建議等待股價跌至更好的買點,或等待市場情緒好轉後再進場。"
recommendations['risk_level'] = "中高風險"
recommendations['action_priority'] = "中優先級 - 傾向防守"
recommendations['position_sizing'] = "建議倉位:20-40%"
else:
# 不確定/盤整
if confidence < 0.2:
# 低信心度
recommendations['holding_advice'] = "🤔 **觀望持有**:AI預測信心度較低,建議維持現狀,等待更明確的訊號。"
recommendations['non_holding_advice'] = "⏳ **保持觀望**:市場方向不明,建議等待更清晰的進場訊號,避免盲目進場。"
recommendations['risk_level'] = "不確定風險"
recommendations['action_priority'] = "低優先級 - 建議觀望"
recommendations['position_sizing'] = "建議倉位:維持現狀"
else:
# 中等信心度的盤整
recommendations['holding_advice'] = "📊 **區間持有**:股價可能在區間震蕩,可在高點減持、低點加碼,執行區間操作策略。"
recommendations['non_holding_advice'] = "🎯 **等待機會**:可在支撐位附近小量進場,等待突破訊號再決定是否加碼。"
recommendations['risk_level'] = "中等風險"
recommendations['action_priority'] = "中優先級 - 區間操作"
recommendations['position_sizing'] = "建議倉位:30-50%"
# RSI 超買超賣特殊建議
if rsi > 80:
recommendations['holding_advice'] += f"\n⚠️ **RSI超買警告**:RSI({rsi:.1f})顯示超買,注意短期回調風險。"
recommendations['non_holding_advice'] += f"\n⚠️ **等待回調**:RSI({rsi:.1f})超買,建議等待回調後再進場。"
elif rsi < 20:
recommendations['holding_advice'] += f"\n💎 **RSI超賣機會**:RSI({rsi:.1f})顯示超賣,可能有反彈機會。"
recommendations['non_holding_advice'] += f"\n💎 **超賣買點**:RSI({rsi:.1f})超賣,可能是不錯的買點。"
return recommendations
def calculate_10day_trend(self, data):
"""計算近10日趨勢 - 由 Copilot 生成"""
if data is None or len(data) < 10:
return "N/A", "數據不足"
# 計算近10日價格變化
close_prices = data['Close'].tail(10)
start_price = close_prices.iloc[0]
end_price = close_prices.iloc[-1]
# 計算變化幅度
change_pct = ((end_price - start_price) / start_price) * 100
# 判斷趨勢
if change_pct >= 3:
trend = "📈↗"
description = f"上漲 {change_pct:.1f}%"
elif change_pct <= -3:
trend = "📉↘"
description = f"下跌 {abs(change_pct):.1f}%"
else:
trend = "➡️↔"
description = f"震盪 {change_pct:+.1f}%"
return trend, description
def calculate_90day_high_breakthrough(self, data):
"""計算90日高點突破情況 - 由 Copilot 生成"""
if data is None or len(data) < 90:
return "❌", "數據不足"
# 獲取近90日數據
last_90_days = data.tail(90)
current_price = last_90_days['Close'].iloc[-1]
# 計算90日內最高價(不包含當日)
high_90_days = last_90_days['Close'].iloc[:-1].max()
# 判斷是否突破
if current_price >= high_90_days:
# 當前價格等於或超過90日高點
return "🚀", f"突破90日高點"
else:
# 計算距離高點差距百分比
gap_pct = ((high_90_days - current_price) / current_price) * 100
return "📊", f"距高點 -{gap_pct:.1f}%"
def check_alerts(self, recent_data, symbol):
"""檢查七項 alert 功能加總結 - 由 Copilot 生成"""
alerts = {
'news_alert': {'status': 'N', 'message': '暫無重大公告'},
'volume_alert1': {'status': 'N', 'message': '量能正常'},
'institutional_alert1': {'status': 'N', 'message': '法人未連續買超'},
'institutional_alert2': {'status': 'N', 'message': '外資未淨買超'},
'technical_alert2': {'status': 'N', 'message': 'MACD/OBV未上升'},
'technical_alert3': {'status': 'N', 'message': '未站上MA5'},
'technical_alert4': {'status': 'N', 'message': '未站上MA10'},
'technical_alert5': {'status': 'N', 'message': '未站上MA20'},
'summary': {'status': '0', 'message': '綠燈總數:0/8'}
}
try:
latest = recent_data.iloc[-1]
# 1. 新聞/公告 alert:公司重大公告(併購、BOT、合約、目標價上修)
# 模擬重大新聞檢查(實際應用中需要接入新聞 API)
import random
news_scenarios = [
f"{symbol} 宣布重大併購案,預計將擴大營運規模",
f"{symbol} 獲得政府 BOT 案標案,合約金額達數十億",
f"{symbol} 簽署重要合作協議,拓展海外市場",
f"分析師大幅上修 {symbol} 目標價,看好未來發展",
f"{symbol} 技術突破獲得認證,將帶動業績成長"
]
# 隨機模擬是否有重大新聞(實際應用中應該從新聞 API 獲取)
if random.random() < 0.3: # 30% 機率有重大新聞
news_title = random.choice(news_scenarios)
alerts['news_alert'] = {
'status': 'Y',
'message': f'重大公告:{news_title}'
}
# 2. 量能 Alert1:當日量 >= 2×20日平均量
if 'Volume' in recent_data.columns:
current_volume = latest['Volume']
avg_20_volume = recent_data['Volume'].rolling(window=20).mean().iloc[-1]
if current_volume >= 2 * avg_20_volume:
alerts['volume_alert1'] = {
'status': 'Y',
'message': f'量能爆發(當日量:{current_volume:,.0f} >= 2×20日均量:{avg_20_volume:,.0f})'
}
else:
alerts['volume_alert1'] = {
'status': 'N',
'message': f'量能正常(當日量:{current_volume:,.0f},20日均量:{avg_20_volume:,.0f})'
}
# 4. 籌碼 Alert1:三大法人連續買超
# 模擬三大法人買賣超資料(實際應用中需要接入相關 API)
consecutive_days = random.randint(1, 5)
if consecutive_days >= 3:
alerts['institutional_alert1'] = {
'status': 'Y',
'message': f'三大法人連續 {consecutive_days} 日買超,籌碼面偏多'
}
else:
alerts['institutional_alert1'] = {
'status': 'N',
'message': f'三大法人買超僅 {consecutive_days} 日,未達連續買超標準'
}
# 5. 籌碼 Alert2:外資當日淨買超
foreign_net_buy = random.choice([True, False])
foreign_amount = random.randint(50, 500) * 1000000 # 5千萬到5億
if foreign_net_buy:
alerts['institutional_alert2'] = {
'status': 'Y',
'message': f'外資當日大幅淨買超 {foreign_amount/100000000:.1f} 億,資金流入明顯'
}
else:
alerts['institutional_alert2'] = {
'status': 'N',
'message': f'外資當日淨賣超 {foreign_amount/100000000:.1f} 億,資金流出'
}
# 3. 技術 Alert2:MACD交叉/OBV上升
technical_conditions = []
# 檢查 MACD 交叉
macd_crossover = False
if 'MACD' in latest and 'MACD_signal' in latest:
current_macd = latest['MACD']
current_signal = latest['MACD_signal']
prev_macd = recent_data['MACD'].iloc[-2] if len(recent_data) > 1 else current_macd
prev_signal = recent_data['MACD_signal'].iloc[-2] if len(recent_data) > 1 else current_signal
# 檢查黃金交叉
if current_macd > current_signal and prev_macd <= prev_signal:
macd_crossover = True
technical_conditions.append("MACD 黃金交叉")
# 模擬 OBV 上升(實際應用中需要計算 OBV 指標)
obv_rising = random.choice([True, False])
if obv_rising:
technical_conditions.append("OBV 上升")
if macd_crossover or obv_rising:
alerts['technical_alert2'] = {
'status': 'Y',
'message': f'技術指標轉強({"/".join(technical_conditions)}),考慮分批進場'
}
else:
alerts['technical_alert2'] = {
'status': 'N',
'message': 'MACD 未交叉且 OBV 未上升,技術面待觀察'
}
# 4. 技術 Alert3:是否站上MA5
current_price = latest['Close']
ma5 = latest.get('MA5', 0)
if current_price > ma5:
alerts['technical_alert3'] = {
'status': 'Y',
'message': f'站上MA5(當前:{current_price:.2f} > MA5:{ma5:.2f})'
}
else:
alerts['technical_alert3'] = {
'status': 'N',
'message': f'未站上MA5(當前:{current_price:.2f},MA5:{ma5:.2f})'
}
# 5. 技術 Alert4:是否站上MA10
ma10 = latest.get('MA10', 0)
if current_price > ma10:
alerts['technical_alert4'] = {
'status': 'Y',
'message': f'站上MA10(當前:{current_price:.2f} > MA10:{ma10:.2f})'
}
else:
alerts['technical_alert4'] = {
'status': 'N',
'message': f'未站上MA10(當前:{current_price:.2f},MA10:{ma10:.2f})'
}
# 6. 技術 Alert5:是否站上MA20
ma20 = latest.get('MA20', 0)
if current_price > ma20:
alerts['technical_alert5'] = {
'status': 'Y',
'message': f'站上MA20(當前:{current_price:.2f} > MA20:{ma20:.2f})'
}
else:
alerts['technical_alert5'] = {
'status': 'N',
'message': f'未站上MA20(當前:{current_price:.2f},MA20:{ma20:.2f})'
}
# 總結:計算所有alert為綠燈(Y)的數量
green_count = sum(1 for alert in alerts.values() if alert['status'] == 'Y')
total_alerts = len([k for k in alerts.keys() if k != 'summary']) # 排除summary本身
alerts['summary'] = {
'status': str(green_count),
'message': f'綠燈總數:{green_count}/{total_alerts}個'
}
except Exception as e:
print(f"Alert 檢查發生錯誤: {e}")
return alerts
def generate_comprehensive_prediction(self, technical_signals, sentiment, recent_data):
"""生成綜合預測報告 - 由 Copilot 生成"""
# 計算價格變化
price_change = ((recent_data['Close'].iloc[-1] - recent_data['Close'].iloc[-5]) / recent_data['Close'].iloc[-5]) * 100
# 計算預測機率
probabilities = self.calculate_prediction_probabilities(technical_signals, sentiment, recent_data)
# 確定主要預測方向
max_prob = max(probabilities['up'], probabilities['down'], probabilities['sideways'])
if probabilities['up'] == max_prob:
main_direction = "看多"
direction_emoji = "📈"
elif probabilities['down'] == max_prob:
main_direction = "看空"
direction_emoji = "📉"
else:
main_direction = "盤整"
direction_emoji = "➡️"
# 信心度描述
confidence = probabilities['confidence']
if confidence > 0.4:
confidence_desc = "高信心"
elif confidence > 0.2:
confidence_desc = "中等信心"
else:
confidence_desc = "低信心"
# 生成買賣建議
buy_sell_rec = self.generate_buy_sell_recommendation(probabilities, confidence, recent_data)
# 檢查四項 alert
alerts = self.check_alerts(recent_data, self.symbol)
report = f"""
## 📊 {self.symbol} AI 分析報告
### 🚨 重要 Alert 提醒:
#### 📰 新聞/公告 Alert:{alerts['news_alert']['status']}
{alerts['news_alert']['message']}
#### 📊 量能 Alert1(量能檢查):{alerts['volume_alert1']['status']}
{alerts['volume_alert1']['message']}
#### 💰 籌碼 Alert1(法人買超):{alerts['institutional_alert1']['status']}
{alerts['institutional_alert1']['message']}
#### 💰 籌碼 Alert2(外資買超):{alerts['institutional_alert2']['status']}
{alerts['institutional_alert2']['message']}
#### 📈 技術 Alert2(MACD/OBV):{alerts['technical_alert2']['status']}
{alerts['technical_alert2']['message']}
#### 📈 技術 Alert3(站上MA5):{alerts['technical_alert3']['status']}
{alerts['technical_alert3']['message']}
#### 📈 技術 Alert4(站上MA10):{alerts['technical_alert4']['status']}
{alerts['technical_alert4']['message']}
#### 📈 技術 Alert5(站上MA20):{alerts['technical_alert5']['status']}
{alerts['technical_alert5']['message']}
#### 🔢 Alert總結:{alerts['summary']['status']}
{alerts['summary']['message']}
---
### 📈 技術面分析:
{chr(10).join(f"• {signal}" for signal in technical_signals)}
### 💭 市場情感:{sentiment}
### 📊 近期表現:
- 5日漲跌幅:{price_change:+.2f}%
- 當前價位:${recent_data['Close'].iloc[-1]:.2f}
### 🤖 AI 預測機率(短期 1-7天):
| 方向 | 機率 | 說明 |
|------|------|------|
| 📈 **上漲** | **{probabilities['up']:.1f}%** | 股價向上突破的可能性 |
| 📉 **下跌** | **{probabilities['down']:.1f}%** | 股價向下修正的可能性 |
| ➡️ **盤整** | **{probabilities['sideways']:.1f}%** | 股價維持震盪的可能性 |
### 🎯 主要預測方向:
{direction_emoji} **{main_direction}** ({confidence_desc} - {confidence*100:.0f}%)
### � **具體投資建議**
---
#### 🏠 **若您目前持有此股票:**
{buy_sell_rec['holding_advice']}
#### 💰 **若您目前未持有此股票:**
{buy_sell_rec['non_holding_advice']}
#### 📊 **投資參數建議:**
- **風險等級:** {buy_sell_rec['risk_level']}
- **操作優先級:** {buy_sell_rec['action_priority']}
- **{buy_sell_rec['position_sizing']}**
### 📋 一般性投資策略:
"""
# 根據最高機率給出一般策略
if probabilities['up'] > 50:
report += """
- 💡 **多頭策略**:考慮逢低加碼或持有現有部位
- 🎯 **目標設定**:關注上方阻力位,設定合理獲利目標
- 🛡️ **風險管理**:設置止損點保護資本"""
elif probabilities['down'] > 50:
report += """
- 💡 **防守策略**:考慮減碼或等待更佳進場點
- 🎯 **支撐觀察**:留意下方支撐位是否守住
- 🛡️ **風險管理**:避免追高,控制倉位大小"""
else:
report += """
- 💡 **中性策略**:保持觀望,等待明確方向訊號
- 🎯 **區間操作**:可考慮在支撐阻力區間內操作
- 🛡️ **風險管理**:小部位測試,嚴格執行停損"""
report += f"""
### 📅 中期展望(1個月):
基於當前技術面和市場情緒分析,建議持續關注:
- 關鍵技術位:支撐與阻力區間
- 市場情緒變化:新聞面和資金流向
- 整體大盤走勢:系統性風險評估
⚠️ **重要風險提醒**:
- 此分析基於歷史數據和 AI 模型預測,僅供參考
- 投資有風險,請謹慎評估並做好風險管理
- 建議結合個人財務狀況和投資目標做決策
- 請勿將此作為唯一投資依據
---
*預測信心度:{confidence*100:.0f}% | 分析時間:{datetime.now().strftime('%Y-%m-%d %H:%M')} | 由 Copilot 生成*
"""
return report
def generate_prediction(self, df, news_sentiment):
"""生成預測分析"""
if df is None or len(df) < 30:
return "數據不足,無法進行預測分析"
# 獲取最新數據
latest = df.iloc[-1]
recent_data = df.tail(20)
# 技術分析信號
technical_signals = []
# 價格趋势
if latest['Close'] > latest['MA20']:
technical_signals.append("價格在20日均線之上(多頭信號)")
else:
technical_signals.append("價格在20日均線之下(空頭信號)")
# RSI 分析
rsi = latest['RSI']
if rsi > 70:
technical_signals.append(f"RSI({rsi:.1f}) 超買警訊")
elif rsi < 30:
technical_signals.append(f"RSI({rsi:.1f}) 超賣機會")
else:
technical_signals.append(f"RSI({rsi:.1f}) 正常範圍")
# MACD 分析
if latest['MACD'] > latest['MACD_signal']:
technical_signals.append("MACD 呈現多頭排列")
else:
technical_signals.append("MACD 呈現空頭排列")
# 新聞情感分析
sentiment_summary = self.analyze_sentiment_summary(news_sentiment)
# 綜合預測
prediction = self.generate_comprehensive_prediction(technical_signals, sentiment_summary, recent_data)
return prediction
# 創建分析器實例
analyzer = StockAnalyzer()
def analyze_stock(symbol):
"""主要分析函數"""
if not symbol.strip():
return None, "請輸入股票代碼", ""
# 獲取數據
result = analyzer.fetch_stock_data(symbol.upper())
if len(result) == 3:
success, message, stock_name = result
else:
success, message = result
stock_name = None
if not success:
return None, message, ""
# 計算技術指標
df = analyzer.calculate_technical_indicators()
# 創建價格圖表
fig = go.Figure()
# 添加K線圖
fig.add_trace(go.Candlestick(
x=df.index,
open=df['Open'],
high=df['High'],
low=df['Low'],
close=df['Close'],
name='價格'
))
# 添加移動平均線
fig.add_trace(go.Scatter(x=df.index, y=df['MA5'], name='MA5', line=dict(color='orange')))
fig.add_trace(go.Scatter(x=df.index, y=df['MA20'], name='MA20', line=dict(color='blue')))
fig.update_layout(
title=f'{symbol} 股價走勢與技術指標',
xaxis_title='日期',
yaxis_title='價格',
height=600
)
# 獲取新聞情感
news_sentiment = analyzer.get_news_sentiment(symbol)
# 生成預測
prediction = analyzer.generate_prediction(df, news_sentiment)
return fig, "分析完成!", prediction
def get_chinese_name(symbol):
"""獲取股票中文簡稱 - 由 Copilot 生成"""
# 台股股票中文名稱對照表
tw_stock_names = {
'2330.TW':'台積電',
'2317.TW':'鴻海',
'2308.TW':'台達電',
'2454.TW':'聯發科',
'2881.TW':'富邦金',
'2382.TW':'廣達',
'2412.TW':'中華電',
'2882.TW':'國泰金',
'2891.TW':'中信金',
'3711.TW':'日月光投控',
'2886.TW':'兆豐金',
'6669.TW':'緯穎',
'2345.TW':'智邦',
'2303.TW':'聯電',
'2884.TW':'玉山金',
'2357.TW':'華碩',
'2885.TW':'元大金',
'2887.TW':'台新新光金',
'3231.TW':'緯創',
'1216.TW':'統一',
'2892.TW':'第一金',
'2383.TW':'台光電',
'2880.TW':'華南金',
'3045.TW':'台灣大',
'2301.TW':'光寶科',
'2603.TW':'長榮',
'6505.TW':'台塑化',
'5880.TW':'合庫金',
'3017.TW':'奇鋐',
'2327.TW':'國巨*',
'3653.TW':'健策',
'2207.TW':'和泰車',
'2890.TW':'永豐金',
'3008.TW':'大立光',
'2059.TW':'川湖',
'1303.TW':'南亞',
'4904.TW':'遠傳',
'2002.TW':'中鋼',
'6919.TW':'康霈*',
'2379.TW':'瑞昱',
'2395.TW':'研華',
'3661.TW':'世芯-KY',
'3034.TW':'聯詠',
'2912.TW':'統一超',
'2883.TW':'凱基金',
'2360.TW':'致茂',
'1301.TW':'台塑',
'3037.TW':'欣興',
'2801.TW':'彰銀',
'2408.TW':'南亞科',
'2368.TW':'金像電',
'2615.TW':'萬海',
'2618.TW':'長榮航',
'5876.TW':'上海商銀',
'1504.TW':'東元',
'2449.TW':'京元電子',
'2376.TW':'技嘉',
'3665.TW':'貿聯-KY',
'6446.TW':'藥華藥',
'5871.TW':'中租-KY',
'4938.TW':'和碩',
'2609.TW':'陽明',
'1519.TW':'華城',
'1101.TW':'台泥',
'3443.TW':'創意',
'1326.TW':'台化',
'3533.TW':'嘉澤',
'2404.TW':'漢唐',
'2356.TW':'英業達',
'4958.TW':'臻鼎-KY',
'3044.TW':'健鼎',
'3036.TW':'文曄',
'2344.TW':'華邦電',
'2834.TW':'臺企銀',
'1590.TW':'亞德客-KY',
'1402.TW':'遠東新',
'8046.TW':'南電',
'2324.TW':'仁寶',
'1102.TW':'亞泥',
'2812.TW':'台中銀',
'2610.TW':'華航',
'8464.TW':'億豐',
'2105.TW':'正新',
'9910.TW':'豐泰',
'1476.TW':'儒鴻',
'2474.TW':'可成',
'3706.TW':'神達',
'3481.TW':'群創',
'1605.TW':'華新',
'3702.TW':'大聯大',
'6239.TW':'力成',
'5269.TW':'祥碩',
'6770.TW':'力積電',
'2347.TW':'聯強',
'2385.TW':'群光',
'2409.TW':'友達',
'6415.TW':'矽力*-KY',
'2354.TW':'鴻準',
'2027.TW':'大成鋼',
'6781.TW':'AES-KY',
'2377.TW':'微星',
'3005.TW':'神基',
'1503.TW':'士電',
'2353.TW':'宏碁',
'6789.TW':'采鈺',
'2542.TW':'興富發',
'1229.TW':'聯華',
'2313.TW':'華通',
'6526.TW':'達發',
'2371.TW':'大同',
'6472.TW':'保瑞',
'9904.TW':'寶成',
'9945.TW':'潤泰新',
'6139.TW':'亞翔',
'6515.TW':'穎崴',
'6409.TW':'旭隼',
'1802.TW':'台玻',
'1513.TW':'中興電',
'2838.TW':'聯邦銀',
'2049.TW':'上銀',
'2646.TW':'星宇航空',
'2633.TW':'台灣高鐵',
'8210.TW':'勤誠',
'1477.TW':'聚陽',
'1795.TW':'美時',
'6805.TW':'富世達',
'2258.TW':'鴻華先進-創',
'6176.TW':'瑞儀',
'8454.TW':'富邦媒',
'2809.TW':'京城銀',
'4763.TW':'材料*-KY',
'2645.TW':'長榮航太',
'2845.TW':'遠東銀',
'6442.TW':'光聖',
'5434.TW':'崇越',
'6191.TW':'精成科',
'9941.TW':'裕融',
'2352.TW':'佳世達',
'2915.TW':'潤泰全',
'2539.TW':'櫻花建',
'6285.TW':'啟碁',
'2889.TW':'國票金',
'6531.TW':'愛普*',
'3023.TW':'信邦',
'1319.TW':'東陽',
'4583.TW':'台灣精銳',
'6890.TW':'來億-KY',
'6691.TW':'洋基工程',
'2540.TW':'愛山林',
'6005.TW':'群益證',
'2634.TW':'漢翔',
'1736.TW':'喬山',
'3376.TW':'新日興',
'2492.TW':'華新科',
'9917.TW':'中保科',
'2498.TW':'宏達電',
'2206.TW':'三陽工業',
'6196.TW':'帆宣',
'1560.TW':'中砂',
'3189.TW':'景碩',
'5522.TW':'遠雄',
'3596.TW':'智易',
'3406.TW':'玉晶光',
'2606.TW':'裕民',
'1722.TW':'台肥',
'1717.TW':'長興',
'2597.TW':'潤弘',
'2451.TW':'創見',
'4766.TW':'南寶',
'2337.TW':'旺宏',
'1210.TW':'大成',
'9958.TW':'世紀鋼',
'5469.TW':'瀚宇博',
'2006.TW':'東和鋼鐵',
'3019.TW':'亞光',
'6257.TW':'矽格',
'2637.TW':'慧洋-KY',
'6592.TW':'和潤企業',
'2504.TW':'國產',
'1907.TW':'永豐餘',
'3030.TW':'德律',
'1215.TW':'卜蜂',
'3035.TW':'智原',
'7722.TW':'LINEPAY',
'6414.TW':'樺漢',
'1773.TW':'勝一',
'2211.TW':'長榮鋼',
'5234.TW':'達興材料',
'6944.TW':'兆聯實業',
'6412.TW':'群電',
'3450.TW':'聯鈞',
'2458.TW':'義隆',
'2543.TW':'皇昌',
'3532.TW':'台勝科',
'6213.TW':'聯茂',
'1231.TW':'聯華食',
'2850.TW':'新產',
'2015.TW':'豐興',
'2923.TW':'鼎固-KY',
'9939.TW':'宏全',
'2855.TW':'統一證',
'2607.TW':'榮運',
'2201.TW':'裕隆',
'9921.TW':'巨大',
'4915.TW':'致伸',
'6670.TW':'復盛應用',
'2441.TW':'超豐',
'6757.TW':'台灣虎航',
'2204.TW':'中華',
'8926.TW':'台汽電',
'2421.TW':'建準',
'2867.TW':'三商壽',
'3515.TW':'華擎',
'6214.TW':'精誠',
'2903.TW':'遠百',
'3583.TW':'辛耘',
'1609.TW':'大亞',
'8028.TW':'昇陽半導體',
'2388.TW':'威盛',
'2548.TW':'華固',
'6278.TW':'台表科',
'5388.TW':'中磊',
'9914.TW':'美利達',
'8996.TW':'高力',
'3413.TW':'京鼎',
'2023.TW':'燁輝',
'2467.TW':'志聖',
'2455.TW':'全新',
'2363.TW':'矽統',
'2312.TW':'金寶',
'3042.TW':'晶技',
'8070.TW':'長華*',
'1808.TW':'潤隆',
'6550.TW':'北極星藥業-KY',
'2393.TW':'億光',
'9907.TW':'統一實',
'9933.TW':'中鼎',
'2328.TW':'廣宇',
'2208.TW':'台船',
'1514.TW':'亞力',
'2374.TW':'佳能',
'2101.TW':'南港',
'1227.TW':'佳格',
'1314.TW':'中石化',
'2501.TW':'國建',
'2481.TW':'強茂',
'4919.TW':'新唐',
'2329.TW':'華泰',
'3714.TW':'富采',
'6282.TW':'康舒',
'8016.TW':'矽創',
'2362.TW':'藍天',
'2439.TW':'美律',
'1434.TW':'福懋',
'2897.TW':'王道銀行',
'2707.TW':'晶華',
'2530.TW':'華建',
'6491.TW':'晶碩',
'3715.TW':'定穎投控',
'3010.TW':'華立',
'2359.TW':'所羅門',
'2392.TW':'正崴',
'2247.TW':'汎德永業',
'6271.TW':'同欣電',
'3013.TW':'晟銘電',
'6116.TW':'彩晶',
'6949.TW':'沛爾生醫-創',
'4770.TW':'上品',
'7749.TW':'意騰-KY',
'1232.TW':'大統益',
'3563.TW':'牧德',
'8112.TW':'至上',
'6177.TW':'達麗',
'1409.TW':'新纖',
'2014.TW':'中鴻',
'8033.TW':'雷虎',
'8422.TW':'可寧衛',
'1440.TW':'南紡',
'3014.TW':'聯陽',
'2476.TW':'鉅祥',
'5534.TW':'長虹',
'2520.TW':'冠德',
'2428.TW':'興勤',
'2836.TW':'高雄銀',
'2820.TW':'華票',
'6605.TW':'帝寶',
'8478.TW':'東哥遊艇',
'6456.TW':'GIS-KY',
'3592.TW':'瑞鼎',
'8150.TW':'南茂',
'1104.TW':'環泥',
'3006.TW':'晶豪科',
'6962.TW':'奕力-KY',
'4961.TW':'天鈺',
'6768.TW':'志強-KY',
'6579.TW':'研揚',
'1608.TW':'華榮',
'6269.TW':'台郡',
'8081.TW':'致新',
'9802.TW':'鈺齊-KY',
'2849.TW':'安泰銀',
'6197.TW':'佳必琪',
'3617.TW':'碩天',
'4722.TW':'國精化',
'2727.TW':'王品',
'1904.TW':'正隆',
'1723.TW':'中碳',
'2106.TW':'建大',
'1707.TW':'葡萄王',
'2851.TW':'中再保',
'3029.TW':'零壹',
'5607.TW':'遠雄港',
'9937.TW':'全國',
'9911.TW':'櫻花',
'2367.TW':'燿華',
'2480.TW':'敦陽科',
'2486.TW':'一詮',
'3703.TW':'欣陸',
'1712.TW':'興農',
'6854.TW':'錼創科技-KY創',
'6449.TW':'鈺邦',
'6873.TW':'泓德能源',
'6719.TW':'力智',
'9940.TW':'信義',
'9930.TW':'中聯資源',
'6589.TW':'台康生技',
'2316.TW':'楠梓電',
'1536.TW':'和大',
'4536.TW':'拓凱',
'8114.TW':'振樺電',
'6937.TW':'天虹',
'2524.TW':'京城',
'1419.TW':'新紡',
'2231.TW':'為升',
'6753.TW':'龍德造船',
'6451.TW':'訊芯-KY',
'2227.TW':'裕日車',
'3016.TW':'嘉晶',
'6901.TW':'鑽石投資',
'6869.TW':'雲豹能源',
'2515.TW':'中工',
'2545.TW':'皇翔',
'2535.TW':'達欣工',
'9938.TW':'百和',
'3673.TW':'TPK-KY',
'3026.TW':'禾伸堂',
'2608.TW':'嘉里大榮',
'2402.TW':'毅嘉',
'3059.TW':'華晶科',
'2704.TW':'國賓',
'9925.TW':'新保',
'8039.TW':'台虹',
'3380.TW':'明泰',
'3167.TW':'大量',
'3705.TW':'永信',
'6533.TW':'晶心科',
'9908.TW':'大台北',
'6806.TW':'森崴能源',
'5007.TW':'三星',
'1442.TW':'名軒',
'4551.TW':'智伸科',
'6965.TW':'中傑-KY',
'1234.TW':'黑松',
'2009.TW':'第一銅',
'1789.TW':'神隆',
'6206.TW':'飛捷',
'6183.TW':'關貿',
'2731.TW':'雄獅',
'2511.TW':'太子',
'5284.TW':'jpp-KY',
'4104.TW':'佳醫',
'3545.TW':'敦泰',
'2351.TW':'順德',
'6438.TW':'迅得',
'3708.TW':'上緯投控',
'3305.TW':'昇貿',
'8021.TW':'尖點',
'2472.TW':'立隆電',
'1313.TW':'聯成',
'3416.TW':'融程電',
'8131.TW':'福懋科',
'3090.TW':'日電貿',
'3704.TW':'合勤控',
'4576.TW':'大銀微系統',
'2355.TW':'敬鵬',
'2464.TW':'盟立',
'2753.TW':'八方雲集',
'2401.TW':'凌陽',
'1903.TW':'士紙',
'4968.TW':'立積',
'6541.TW':'泰福-KY',
'2031.TW':'新光鋼',
'2373.TW':'震旦行',
'2493.TW':'揚博',
'2832.TW':'台產',
'2905.TW':'三商',
'3033.TW':'威健',
'2605.TW':'新興',
'2103.TW':'台橡',
'5243.TW':'乙盛-KY',
'6166.TW':'凌華',
'2010.TW':'春源',
'2723.TW':'美食-KY',
'1909.TW':'榮成',
'1905.TW':'華紙',
'1522.TW':'堤維西',
'2348.TW':'海悅',
'8462.TW':'柏文',
'6782.TW':'視陽',
'6189.TW':'豐藝',
'5222.TW':'全訊',
'6230.TW':'尼得科超眾',
'6235.TW':'華孚',
'2528.TW':'皇普',
'1436.TW':'華友聯',
'4755.TW':'三福化',
'4532.TW':'瑞智',
'6581.TW':'鋼聯',
'3694.TW':'海華',
'2233.TW':'宇隆',
'1726.TW':'永記',
'1612.TW':'宏泰',
'2331.TW':'精英',
'6024.TW':'群益期',
'1307.TW':'三芳',
'1304.TW':'台聚',
'3022.TW':'威強電',
'1702.TW':'南僑',
'2108.TW':'南帝',
'6534.TW':'正瀚-創',
'4736.TW':'泰博',
'1532.TW':'勤美',
'2547.TW':'日勝生',
'2466.TW':'冠西電',
'2546.TW':'根基',
'2630.TW':'亞航',
'2617.TW':'台航',
'7740.TW':'熙特爾-創',
'3576.TW':'聯合再生',
'1563.TW':'巧新',
'1710.TW':'東聯',
'1718.TW':'中纖',
'3605.TW':'宏致',
'5306.TW':'桂盟',
'1103.TW':'嘉泥',
'4906.TW':'正文',
'6525.TW':'捷敏-KY',
'8404.TW':'百和興業-KY',
'1537.TW':'廣隆',
'1711.TW':'永光',
'2104.TW':'國際中橡',
'2612.TW':'中航',
'2436.TW':'偉詮電',
'2332.TW':'友訊',
'1312.TW':'國喬',
'6863.TW':'永道-KY',
'6923.TW':'中台',
'2457.TW':'飛宏',
'2340.TW':'台亞',
'2034.TW':'允強',
'6790.TW':'永豐實',
'8163.TW':'達方',
'8499.TW':'鼎炫-KY',
'4771.TW':'望隼',
'6215.TW':'和椿',
'8341.TW':'日友',
'2338.TW':'光罩',
'2534.TW':'宏盛',
'2908.TW':'特力',
'3032.TW':'偉訓',
'3015.TW':'全漢',
'1720.TW':'生達',
'1582.TW':'信錦',
'1614.TW':'三洋電',
'2429.TW':'銘旺科',
'2913.TW':'農林',
'2636.TW':'台驊控股',
'6192.TW':'巨路',
'3048.TW':'益登',
'1235.TW':'興泰',
'2323.TW':'中環',
'4977.TW':'眾達-KY',
'6464.TW':'台數科',
'4306.TW':'炎洲',
'3701.TW':'大眾控',
'6153.TW':'嘉聯益',
'6957.TW':'裕慶-KY',
'1604.TW':'聲寶',
'1218.TW':'泰山',
'3056.TW':'富華新',
'1203.TW':'味王',
'1615.TW':'大山',
'6914.TW':'阜爾運通',
'8213.TW':'志超',
'9927.TW':'泰銘',
'6202.TW':'盛群',
'3645.TW':'達邁',
'1110.TW':'東泥',
'3149.TW':'正達',
'8110.TW':'華東',
'6655.TW':'科定',
'4571.TW':'鈞興-KY',
'2365.TW':'昆盈',
'2536.TW':'宏普',
'9942.TW':'茂順',
'4526.TW':'東台',
'2489.TW':'瑞軒',
'5225.TW':'東科-KY',
'5525.TW':'順天',
'9926.TW':'新海',
'5531.TW':'鄉林',
'6115.TW':'鎰勝',
'1618.TW':'合機',
'2102.TW':'泰豐',
'8374.TW':'羅昇',
'8467.TW':'波力-KY',
'4164.TW':'承業醫',
'3712.TW':'永崴投控',
'2375.TW':'凱美',
'4137.TW':'麗豐-KY',
'6830.TW':'汎銓',
'9924.TW':'福興',
'5258.TW':'虹堡',
'2013.TW':'中鋼構',
'2228.TW':'劍麟',
'8045.TW':'達運光電',
'4739.TW':'康普',
'2762.TW':'世界健身-KY',
'1321.TW':'大洋',
'4960.TW':'誠美材',
'3622.TW':'洋華',
'9943.TW':'好樂迪',
'8271.TW':'宇瞻',
'4142.TW':'國光生',
'3003.TW':'健和興',
'2423.TW':'固緯',
'2387.TW':'精元',
'2852.TW':'第一保',
'1714.TW':'和桐',
'2420.TW':'新巨',
'4438.TW':'廣越',
'3454.TW':'晶睿',
'8261.TW':'富鼎',
'4746.TW':'台耀',
'6184.TW':'大豐電',
'2397.TW':'友通',
'3062.TW':'建漢',
'1709.TW':'和益',
'1201.TW':'味全',
'5203.TW':'訊連',
'5288.TW':'豐祥-KY',
'6443.TW':'元晶',
'3312.TW':'弘憶股',
'8249.TW':'菱光',
'8215.TW':'明基材',
'6933.TW':'AMAX-KY',
'6117.TW':'迎廣',
'1603.TW':'華電',
'1708.TW':'東鹼',
'3004.TW':'豐達科',
'2478.TW':'大毅',
'3130.TW':'一零四',
'6277.TW':'宏正',
'4967.TW':'十銓',
'6862.TW':'三集瑞-KY',
'6585.TW':'鼎基',
'3028.TW':'增你強',
'2419.TW':'仲琦',
'2450.TW':'神腦',
'3209.TW':'全科',
'1713.TW':'國化',
'1786.TW':'科妍',
'2107.TW':'厚生',
'6958.TW':'日盛台駿',
'3679.TW':'新至陞',
'4119.TW':'旭富',
'9918.TW':'欣天然',
'2029.TW':'盛餘',
'1225.TW':'福懋油',
'2614.TW':'東森',
'2406.TW':'國碩',
'2369.TW':'菱生',
'2505.TW':'國揚',
'1597.TW':'直得',
'2349.TW':'錸德',
'4927.TW':'泰鼎-KY',
'7736.TW':'虎山',
'6706.TW':'惠特',
'2114.TW':'鑫永銓',
'2414.TW':'精技',
'2433.TW':'互盛電',
'1308.TW':'亞聚',
'2442.TW':'新美齊',
'2527.TW':'宏璟',
'6281.TW':'全國電',
'6120.TW':'達運',
'3504.TW':'揚明光',
'6672.TW':'騰輝電子-KY',
'1342.TW':'八貫',
'1535.TW':'中宇',
'1616.TW':'億泰',
'2459.TW':'敦吉',
'2706.TW':'第一店',
'6994.TW':'富威電力',
'5608.TW':'四維航',
'4994.TW':'傳奇',
'6209.TW':'今國光',
'1305.TW':'華夏',
'2020.TW':'美亞',
'2417.TW':'圓剛',
'2405.TW':'輔信',
'4572.TW':'駐龍',
'4916.TW':'事欣科',
'5292.TW':'華懋',
'5519.TW':'隆大',
'8473.TW':'山林水',
'1737.TW':'臺鹽',
'1315.TW':'達新',
'1444.TW':'力麗',
'9905.TW':'大華',
'5471.TW':'松翰',
'3535.TW':'晶彩科',
'6112.TW':'邁達特',
'6715.TW':'嘉基',
'1437.TW':'勤益控',
'2453.TW':'凌群',
'2495.TW':'普安',
'1108.TW':'幸福',
'6756.TW':'威鋒電子',
'6416.TW':'瑞祺電通',
'3501.TW':'維熹',
'1558.TW':'伸興',
'1760.TW':'寶齡富錦',
'2497.TW':'怡利電',
'2816.TW':'旺旺保',
'4935.TW':'茂林-KY',
'5521.TW':'工信',
'8105.TW':'凌巨',
'9946.TW':'三發地產',
'4566.TW':'時碩工業',
'2514.TW':'龍邦',
'2236.TW':'百達-KY',
'1734.TW':'杏輝',
'1810.TW':'和成',
'1256.TW':'鮮活果汁-KY',
'5538.TW':'東明-KY',
'8482.TW':'商億-KY',
'1316.TW':'上曜',
'1583.TW':'程泰',
'2028.TW':'威致',
'3055.TW':'蔚華科',
'3257.TW':'虹冠電',
'4976.TW':'佳凌',
'3049.TW':'精金',
'2426.TW':'鼎元',
'1109.TW':'信大',
'1457.TW':'宜進',
'2427.TW':'三商電',
'3447.TW':'展達',
'6887.TW':'寶綠特-KY',
'9906.TW':'欣巴巴',
'6477.TW':'安集',
'1447.TW':'力鵬',
'1515.TW':'力山',
'1217.TW':'愛之味',
'2012.TW':'春雨',
'2008.TW':'高興昌',
'6201.TW':'亞弘電',
'3338.TW':'泰碩',
'4942.TW':'嘉彰',
'4912.TW':'聯德控股-KY',
'6928.TW':'攸泰科技',
'6689.TW':'伊雲谷',
'2342.TW':'茂矽',
'2701.TW':'萬企',
'1525.TW':'江申',
'8103.TW':'瀚荃',
'3266.TW':'昇陽',
'3530.TW':'晶相光',
'3716.TW':'中化控股',
'5285.TW':'界霖',
'5283.TW':'禾聯碩',
'5515.TW':'建國',
'6625.TW':'必應',
'3528.TW':'安馳',
'8442.TW':'威宏-KY',
'1455.TW':'集盛',
'1725.TW':'元禎',
'2748.TW':'雲品',
'3138.TW':'耀登',
'2538.TW':'基泰',
'2601.TW':'益航',
'1464.TW':'得力',
'1472.TW':'三洋實業',
'6743.TW':'安普新',
'6695.TW':'芯鼎',
'6657.TW':'華安',
'4569.TW':'六方科-KY',
'4540.TW':'全球傳動',
'6838.TW':'台新藥',
'1524.TW':'耿鼎',
'1219.TW':'福壽',
'1310.TW':'台苯',
'5706.TW':'鳳凰',
'4564.TW':'元翎',
'4952.TW':'凌通',
'1527.TW':'鑽全',
'1727.TW':'中華化',
'2062.TW':'橋椿',
'2109.TW':'華豐',
'2007.TW':'燁興',
'1459.TW':'聯發',
'1309.TW':'台達化',
'6168.TW':'宏齊',
'6224.TW':'聚鼎',
'6776.TW':'展碁國際',
'6885.TW':'全福生技',
'6955.TW':'邦睿生技-創',
'9934.TW':'成霖',
'6558.TW':'興能高',
'6165.TW':'浪凡',
'5533.TW':'皇鼎',
'4737.TW':'華廣',
'3356.TW':'奇偶',
'1611.TW':'中電',
'2017.TW':'官田鋼',
'2239.TW':'英利-KY',
'2485.TW':'兆赫',
'3047.TW':'訊舟',
'1438.TW':'三地開發',
'1414.TW':'東和',
'8367.TW':'建新國際',
'9931.TW':'欣高',
'3717.TW':'聯嘉投控',
'1460.TW':'宏遠',
'1783.TW':'和康生',
'3040.TW':'遠見',
'2722.TW':'夏都',
'2364.TW':'倫飛',
'2030.TW':'彰源',
'1598.TW':'岱宇',
'1443.TW':'立益物流',
'2254.TW':'巨鎧精密-創',
'2477.TW':'美隆電',
'4934.TW':'太極',
'8104.TW':'錸寶',
'8463.TW':'潤泰材',
'6742.TW':'澤米',
'4562.TW':'穎漢',
'2462.TW':'良得電',
'3168.TW':'眾福科',
'1423.TW':'利華',
'1463.TW':'強盛新',
'1473.TW':'台南',
'1752.TW':'南光',
'3051.TW':'力特',
'3024.TW':'憶聲',
'2613.TW':'中櫃',
'2468.TW':'華經',
'2506.TW':'太設',
'4439.TW':'冠星-KY',
'6799.TW':'來頡',
'6906.TW':'現觀科',
'8011.TW':'台通',
'6794.TW':'向榮生技-創',
'9935.TW':'慶豐富',
'2910.TW':'統領',
'1733.TW':'五鼎',
'1806.TW':'冠軍',
'1451.TW':'年興',
'1529.TW':'樂事綠能',
'1435.TW':'中福',
'2739.TW':'寒舍',
'3031.TW':'佰鴻',
'3060.TW':'銘異',
'2488.TW':'漢平',
'4989.TW':'榮科',
'6128.TW':'上福',
'6205.TW':'詮欣',
'4764.TW':'雙鍵',
'3557.TW':'嘉威',
'3311.TW':'閎暉',
'6668.TW':'中揚光',
'6931.TW':'青松健康',
'2484.TW':'希華',
'2509.TW':'全坤建',
'3046.TW':'建碁',
'3038.TW':'全台',
'2399.TW':'映泰',
'1587.TW':'吉茂',
'6951.TW':'青新-創',
'9919.TW':'康那香',
'4557.TW':'永新-KY',
'6591.TW':'動力-KY',
'6283.TW':'淳安',
'8222.TW':'寶一',
'1730.TW':'花仙子',
'2250.TW':'IKKA-KY',
'2516.TW':'新建',
'2537.TW':'聯上發',
'3041.TW':'揚智',
'2465.TW':'麗臺',
'2241.TW':'艾姆勒',
'1809.TW':'中釉',
'9955.TW':'佳龍',
'6504.TW':'南六',
'3543.TW':'州巧',
'3588.TW':'通嘉',
'8476.TW':'台境*',
'6952.TW':'大武山',
'6918.TW':'愛派司',
'6754.TW':'匯僑設計',
'1589.TW':'永冠-KY',
'1533.TW':'車王電',
'1341.TW':'富林-KY',
'2705.TW':'六福',
'2460.TW':'建通',
'1339.TW':'昭輝',
'7721.TW':'微程式',
'4555.TW':'氣立',
'4414.TW':'如興',
'6582.TW':'申豐',
'4108.TW':'懷特',
'3669.TW':'圓展',
'6909.TW':'創控',
'1528.TW':'恩德',
'2038.TW':'海光',
'2425.TW':'承啟',
'2906.TW':'高林',
'2611.TW':'志信',
'2642.TW':'宅配通',
'2430.TW':'燦坤',
'2302.TW':'麗正',
'1817.TW':'凱撒衛',
'1731.TW':'美吾華',
'8481.TW':'政伸',
'5244.TW':'弘凱',
'6606.TW':'建德工業',
'8411.TW':'福貞-KY',
'8162.TW':'微矽電子-創',
'8072.TW':'陞泰',
'6902.TW':'GOGOLOOK',
'2115.TW':'六暉-KY',
'1233.TW':'天仁',
'2413.TW':'環科',
'2461.TW':'光群雷',
'1446.TW':'宏和',
'1617.TW':'榮星',
'6807.TW':'峰源-KY',
'6796.TW':'晉弘',
'6792.TW':'詠業',
'7631.TW':'聚賢研發-創',
'7705.TW':'三商餐飲',
'6426.TW':'統新',
'6136.TW':'富爾特',
'3346.TW':'麗清',
'4545.TW':'銘鈺',
'4588.TW':'玖鼎電力',
'4949.TW':'有成精密',
'4552.TW':'力達-KY',
'4148.TW':'全宇生技-KY',
'3591.TW':'艾笛森',
'5484.TW':'慧友',
'6216.TW':'居易',
'2022.TW':'聚亨',
'1530.TW':'亞崴',
'1325.TW':'恆大',
'1439.TW':'雋揚',
'3052.TW':'夆典',
'3027.TW':'盛達',
'6834.TW':'天二科技',
'6835.TW':'圓裕',
'6861.TW':'睿生光電',
'6936.TW':'永鴻生技',
'3025.TW':'星通',
'2945.TW':'三商家購',
'1721.TW':'三晃',
'2069.TW':'運錩',
'1517.TW':'利奇',
'1568.TW':'倉佑',
'2471.TW':'資通',
'2483.TW':'百容',
'6666.TW':'羅麗芬-KY',
'7732.TW':'金興精密',
'6598.TW':'ABC-KY',
'6552.TW':'易華電',
'6423.TW':'億而得-創',
'6142.TW':'友勁',
'6108.TW':'競國',
'3652.TW':'精聯',
'6698.TW':'旭暉應材',
'2415.TW':'錩新',
'3058.TW':'立德',
'1220.TW':'台榮',
'2305.TW':'全友',
'3094.TW':'聯傑',
'6671.TW':'三能-KY',
'3437.TW':'榮創',
'4106.TW':'雃博',
'4560.TW':'強信-KY',
'6155.TW':'鈞寶',
'5215.TW':'科嘉-KY',
'4155.TW':'訊映',
'4956.TW':'光鋐',
'3518.TW':'柏騰',
'6658.TW':'聯策',
'8487.TW':'愛爾達-創',
'8438.TW':'昶昕',
'1236.TW':'宏亞',
'1416.TW':'廣豐',
'1410.TW':'南染',
'1540.TW':'喬福',
'1475.TW':'業旺',
'1506.TW':'正道',
'1468.TW':'昶和',
'1541.TW':'錩泰',
'1531.TW':'高林股',
'2248.TW':'華勝-KY',
'1906.TW':'寶隆',
'2712.TW':'遠雄來',
'3296.TW':'勝德',
'4190.TW':'佐登-KY',
'5546.TW':'永固-KY',
'6133.TW':'金橋',
'2616.TW':'山隆',
'3054.TW':'立萬利',
'3050.TW':'鈺德',
'2390.TW':'云辰',
'1762.TW':'中化生',
'2025.TW':'千興',
'1521.TW':'大億',
'1470.TW':'大統新創',
'1466.TW':'聚隆',
'1445.TW':'大宇',
'2032.TW':'新鋼',
'2702.TW':'華園',
'2901.TW':'欣欣',
'4999.TW':'鑫禾',
'8466.TW':'美吉吉-KY',
'9944.TW':'新麗',
'4581.TW':'光隆精密-KY',
'4930.TW':'燦星網',
'4720.TW':'德淵',
'1626.TW':'艾美特-KY',
'1454.TW':'台富',
'1338.TW':'廣華-KY',
'1323.TW':'永裕',
'1452.TW':'宏益',
'2033.TW':'佳大',
'3092.TW':'鴻碩',
'3021.TW':'鴻名',
'3011.TW':'今皓',
'2440.TW':'太空梭',
'2432.TW':'倚天酷碁-創',
'4426.TW':'利勤',
'3419.TW':'譁裕',
'5906.TW':'台南-KY',
'6405.TW':'悅城',
'3607.TW':'谷崧',
'3002.TW':'歐格',
'3164.TW':'景岳',
'2024.TW':'志聯',
'1453.TW':'大將',
'1449.TW':'佳和',
'1417.TW':'嘉裕',
'1432.TW':'大魯閣',
'4133.TW':'亞諾法',
'6243.TW':'迅杰',
'6152.TW':'百一',
'9902.TW':'台火',
'6771.TW':'平和環保-創',
'6674.TW':'鋐寶科技',
'8429.TW':'金麗-KY',
'3686.TW':'達能',
'1474.TW':'弘裕',
'1805.TW':'寶徠',
'1735.TW':'日勝化',
'2482.TW':'連宇',
'2438.TW':'翔耀',
'2431.TW':'聯昌',
'1471.TW':'首利',
'1337.TW':'再生-KY',
'6969.TW':'成信實業*-創',
'6645.TW':'金萬林-創',
'8940.TW':'新天地',
'3550.TW':'聯穎',
'6141.TW':'柏承',
'1465.TW':'偉全',
'2904.TW':'匯僑',
'2496.TW':'卓越',
'1467.TW':'南緯',
'1340.TW':'勝悅-KY',
'1732.TW':'毛寶',
'6916.TW':'華凌',
'6924.TW':'榮惠-KY創',
'9928.TW':'中視',
'9929.TW':'秋雨',
'5907.TW':'大洋-KY',
'4440.TW':'宜新實業',
'1456.TW':'怡華',
'1539.TW':'巨庭',
'2444.TW':'兆勁',
'2939.TW':'永邑-KY',
'2491.TW':'吉祥全',
'1413.TW':'宏洲',
'1418.TW':'東華',
'1526.TW':'日馳',
'2424.TW':'隴華',
'2929.TW':'淘帝-KY',
'3150.TW':'鈺寶-創',
'3308.TW':'聯德',
'8201.TW':'無敵',
'3043.TW':'科風',
'1776.TW':'展宇',
'2243.TW':'宏旭-KY',
'1512.TW':'瑞利',
'1324.TW':'地球',
'8443.TW':'阿瘦',
'6988.TW':'威力暘-創',
'6164.TW':'華興',
'6226.TW':'光鼎',
'6431.TW':'光麗-KY',
'6573.TW':'虹揚-KY',
'6641.TW':'基士德-KY',
'8488.TW':'吉源-KY',
'1441.TW':'大東',
'3057.TW':'喬鼎',
'9912.TW':'偉聯',
'3494.TW':'誠研',
'4807.TW':'日成-KY',
'1516.TW':'川飛',
'3229.TW':'晟鈦',
'2434.TW':'統懋',
'3432.TW':'台端',
}
# 美股股票中文名稱對照表
us_stock_names = {
'AAPL': '蘋果',
'MSFT': '微軟',
'GOOGL': '谷歌',
'AMZN': '亞馬遜',
'TSLA': '特斯拉',
'META': 'Meta',
'NVDA': '輝達',
'NFLX': '網飛',
'AMD': '超微',
'INTC': '英特爾',
'ORCL': '甲骨文',
'CRM': 'Salesforce',
'ADBE': 'Adobe',
'PYPL': 'PayPal',
'UBER': 'Uber',
'SPOT': 'Spotify',
'ZOOM': 'Zoom',
'SHOP': 'Shopify',
'SQ': 'Block',
'TWTR': '推特',
'SNAP': 'Snapchat',
'PINS': 'Pinterest',
'DOCU': 'DocuSign',
'ROKU': 'Roku',
'PLTR': 'Palantir',
'COIN': 'Coinbase',
'RBLX': 'Roblox',
'RIVN': 'Rivian',
'LCID': 'Lucid',
'NIO': '蔚來',
'XPEV': '小鵬',
'LI': '理想',
'BABA': '阿里巴巴',
'JD': '京東',
'PDD': '拼多多',
'BIDU': '百度',
'TME': '騰訊音樂',
'BILI': 'B站',
'VTI': '全市場ETF',
'VOO': 'S&P500ETF',
'QQQ': '納斯達克ETF',
'SPY': 'SPDR S&P500',
'IWM': '羅素2000',
'EFA': '歐澳遠東ETF',
'EEM': '新興市場ETF',
'VNQ': '房地產ETF',
'GLD': '黃金ETF',
'TLT': '長期國債ETF',
'BTC-USD': '比特幣',
'ETH-USD': '以太幣',
'^GSPC': 'S&P500指數',
'^DJI': '道瓊指數',
'^IXIC': '納斯達克指數',
}
# 首先查找台股
if symbol in tw_stock_names:
return tw_stock_names[symbol]
# 然後查找美股
if symbol in us_stock_names:
return us_stock_names[symbol]
# 如果都找不到,返回原始名稱(通常是從 yfinance 獲取的英文名稱)
return symbol
def create_results_table(results):
"""創建結果表格 - 由 Copilot 生成"""
if not results:
return ""
# 創建表格 HTML
table_html = """
| 中文名稱 |
股票代號 |
當前價格 |
近10日漲跌 |
90日高點突破 |
上漲機率(%) |
下跌機率(%) |
盤整機率(%) |
信心度(%) |
新聞 |
量能1 |
籌碼1 |
籌碼2 |
技術2 |
技術3 |
技術4 |
技術5 |
總結 |
預測方向 |
持股建議 |
買進建議 |
狀態 |
"""
for result in results:
# 判斷預測方向和顏色
if result['error_message']:
direction = "❌ 錯誤"
row_color = "#fff2f2"
holding_advice = "N/A"
buying_advice = "N/A"
news_alert = "N/A"
volume_alert1 = "N/A"
institutional_alert1 = "N/A"
institutional_alert2 = "N/A"
technical_alert2 = "N/A"
technical_alert3 = "N/A"
technical_alert4 = "N/A"
technical_alert5 = "N/A"
summary_alert = "N/A"
else:
up_prob = float(result['up_probability'])
down_prob = float(result['down_probability'])
sideways_prob = float(result['sideways_probability'])
confidence = float(result['confidence']) / 100.0 # 轉換為小數
if up_prob > down_prob and up_prob > sideways_prob:
direction = "📈 看多"
row_color = "#f0fff0" # 淡綠色
elif down_prob > up_prob and down_prob > sideways_prob:
direction = "📉 看空"
row_color = "#fff0f0" # 淡紅色
else:
direction = "➡️ 盤整"
row_color = "#f8f8f8" # 淡灰色
# 生成簡化的買賣建議
if up_prob >= 60 and confidence >= 0.3:
holding_advice = "🔥 強烈持有"
buying_advice = "🚀 積極買進"
elif up_prob >= 45 and confidence >= 0.25:
holding_advice = "✅ 建議持有"
buying_advice = "💰 適度買進"
elif down_prob >= 60 and confidence >= 0.3:
holding_advice = "🚨 建議賣出"
buying_advice = "⛔ 不建議買進"
elif down_prob >= 45 and confidence >= 0.25:
holding_advice = "⚠️ 謹慎持有"
buying_advice = "🔍 暫緩買進"
else:
if confidence < 0.2:
holding_advice = "🤔 觀望持有"
buying_advice = "⏳ 保持觀望"
else:
holding_advice = "📊 區間持有"
buying_advice = "🎯 等待機會"
# Alert 狀態顯示
news_alert = f"{'🟢' if result.get('news_alert') == 'Y' else '🔴'} {result.get('news_alert', 'N')}"
volume_alert1 = f"{'🟢' if result.get('volume_alert1') == 'Y' else '🔴'} {result.get('volume_alert1', 'N')}"
institutional_alert1 = f"{'🟢' if result.get('institutional_alert1') == 'Y' else '🔴'} {result.get('institutional_alert1', 'N')}"
institutional_alert2 = f"{'🟢' if result.get('institutional_alert2') == 'Y' else '🔴'} {result.get('institutional_alert2', 'N')}"
technical_alert2 = f"{'🟢' if result.get('technical_alert2') == 'Y' else '🔴'} {result.get('technical_alert2', 'N')}"
technical_alert3 = f"{'🟢' if result.get('technical_alert3') == 'Y' else '🔴'} {result.get('technical_alert3', 'N')}"
technical_alert4 = f"{'🟢' if result.get('technical_alert4') == 'Y' else '🔴'} {result.get('technical_alert4', 'N')}"
technical_alert5 = f"{'🟢' if result.get('technical_alert5') == 'Y' else '🔴'} {result.get('technical_alert5', 'N')}"
summary_alert = f"🔢 {result.get('summary', '0')}"
status = "✅ 成功" if not result['error_message'] else f"❌ {result['error_message'][:20]}..."
# 獲取中文名稱
chinese_name = get_chinese_name(result['symbol'])
# 顯示近10日趨勢 - 由 Copilot 生成
trend_display = f"{result.get('trend_10day_icon', 'N/A')} {result.get('trend_10day_desc', '')}"
if result['error_message']:
trend_display = "❌ N/A"
# 顯示90日高點突破 - 由 Copilot 生成
high_90_display = f"{result.get('high_90_icon', 'N/A')} {result.get('high_90_desc', '')}"
if result['error_message']:
high_90_display = "❌ N/A"
table_html += f"""
| {chinese_name} |
{result['symbol']} |
{result['current_price']} |
{trend_display} |
{high_90_display} |
{result['up_probability']} |
{result['down_probability']} |
{result['sideways_probability']} |
{result['confidence']} |
{news_alert} |
{volume_alert1} |
{institutional_alert1} |
{institutional_alert2} |
{technical_alert2} |
{technical_alert3} |
{technical_alert4} |
{technical_alert5} |
{summary_alert} |
{direction} |
{holding_advice} |
{buying_advice} |
{status} |
"""
table_html += """
🚨 十項 Alert 說明:
📰 新聞: 公司重大公告(併購、BOT、合約、目標價上修)
📊 量能1: 當日量 >= 2×20日平均量 | 量能2: 突破價格阻力位(顯示阻力位和目標價)
💰 籌碼1: 三大法人連續買超 | 籌碼2: 外資當日淨買超↑
📈 技術1: 價格突破20日高 | 技術2: MACD交叉/OBV上升
📈 技術3: 是否站上MA5 | 技術4: 是否站上MA10 | 技術5: 是否站上MA20
� 90日高點突破: 🚀 突破90日高點 | 📊 距高點差距百分比
�🔢 總結: 所有Alert為綠燈(Y)的數量加總
🟢 Y = 達到條件 | 🔴 N = 未達到條件
買賣建議說明:
🔥強烈持有/🚀積極買進: 高機率上漲且高信心度
✅建議持有/💰適度買進: 中高機率上漲
🚨建議賣出/⛔不建議買進: 高機率下跌且高信心度
⚠️謹慎持有/🔍暫緩買進: 中高機率下跌
🤔觀望持有/⏳保持觀望: 低信心度預測
📊區間持有/🎯等待機會: 盤整機率較高
註:此建議僅供參考,請結合個人情況謹慎投資 - 由 Copilot 生成
"""
return table_html
def create_batch_analysis_charts(results):
"""創建批次分析結果圖表"""
if not results:
return None, None, None, None
# 過濾出成功分析的結果
success_results = [r for r in results if r['error_message'] == '']
if not success_results:
return None, None, None, None
# 準備數據
symbols = [r['symbol'] for r in success_results]
up_probs = [float(r['up_probability']) for r in success_results]
down_probs = [float(r['down_probability']) for r in success_results]
sideways_probs = [float(r['sideways_probability']) for r in success_results]
confidence = [float(r['confidence']) for r in success_results]
# 1. 機率比較柱狀圖
fig_bar = go.Figure()
fig_bar.add_trace(go.Bar(name='上漲機率', x=symbols, y=up_probs, marker_color='green', opacity=0.8))
fig_bar.add_trace(go.Bar(name='下跌機率', x=symbols, y=down_probs, marker_color='red', opacity=0.8))
fig_bar.add_trace(go.Bar(name='盤整機率', x=symbols, y=sideways_probs, marker_color='gray', opacity=0.8))
fig_bar.update_layout(
title='📊 股票預測機率比較',
xaxis_title='股票代號',
yaxis_title='機率 (%)',
barmode='group',
height=500,
showlegend=True,
xaxis_tickangle=-45
)
# 2. 信心度散佈圖
fig_scatter = go.Figure()
# 根據最高機率決定顏色
colors = []
for i in range(len(success_results)):
if up_probs[i] > down_probs[i] and up_probs[i] > sideways_probs[i]:
colors.append('green') # 看多
elif down_probs[i] > up_probs[i] and down_probs[i] > sideways_probs[i]:
colors.append('red') # 看空
else:
colors.append('gray') # 盤整
fig_scatter.add_trace(go.Scatter(
x=symbols,
y=confidence,
mode='markers+text',
marker=dict(
size=[max(prob) for prob in zip(up_probs, down_probs, sideways_probs)],
sizemode='diameter',
sizeref=2,
color=colors,
opacity=0.7,
line=dict(width=2, color='white')
),
text=[f"{conf:.1f}%" for conf in confidence],
textposition="middle center",
name='信心度'
))
fig_scatter.update_layout(
title='🎯 預測信心度分佈 (圓圈大小=最高機率)',
xaxis_title='股票代號',
yaxis_title='信心度 (%)',
height=500,
xaxis_tickangle=-45
)
# 3. 綜合評分雷達圖 (取前6支股票)
radar_data = success_results[:6] # 限制顯示數量避免過於擁擠
fig_radar = go.Figure()
categories = ['上漲機率', '信心度', '綜合評分']
for i, result in enumerate(radar_data):
# 計算綜合評分 (上漲機率 * 信心度 / 100)
composite_score = float(result['up_probability']) * float(result['confidence']) / 100
values = [
float(result['up_probability']),
float(result['confidence']),
composite_score
]
fig_radar.add_trace(go.Scatterpolar(
r=values + [values[0]], # 閉合雷達圖
theta=categories + [categories[0]],
fill='toself',
name=result['symbol'],
opacity=0.6
))
fig_radar.update_layout(
polar=dict(
radialaxis=dict(
visible=True,
range=[0, 100]
)
),
title='📈 股票綜合評分雷達圖 (前6支)',
height=500,
showlegend=True
)
# 4. 機率分佈餅圖統計
# 統計各種預測傾向的數量
bullish_count = sum(1 for r in success_results if float(r['up_probability']) > max(float(r['down_probability']), float(r['sideways_probability'])))
bearish_count = sum(1 for r in success_results if float(r['down_probability']) > max(float(r['up_probability']), float(r['sideways_probability'])))
neutral_count = len(success_results) - bullish_count - bearish_count
fig_pie = go.Figure(data=[go.Pie(
labels=['看多股票', '看空股票', '盤整股票'],
values=[bullish_count, bearish_count, neutral_count],
marker_colors=['green', 'red', 'gray'],
textinfo='label+percent+value',
hovertemplate='%{label}
數量: %{value}
比例: %{percent}'
)])
fig_pie.update_layout(
title='🥧 整體市場情緒分佈',
height=400
)
return fig_bar, fig_scatter, fig_radar, fig_pie
def batch_analyze_stocks(stock_list_input):
"""批次分析股票清單"""
# 檢查輸入是否為空
if not stock_list_input or not stock_list_input.strip():
return "❌ 請輸入股票代碼!可用逗號、空格或換行分隔多個股票代碼。", "", None, None, None, None, ""
try:
# 解析股票清單(支援多種分隔符)
import re
stock_symbols = re.split(r'[,\s\n]+', stock_list_input.strip())
stock_symbols = [symbol.strip().upper() for symbol in stock_symbols if symbol.strip()]
if not stock_symbols:
return "❌ 未找到有效的股票代碼!", "", None, None, None, None, ""
# 準備結果列表
results = []
progress_messages = []
progress_messages.append(f"📊 開始批次分析 {len(stock_symbols)} 支股票...")
# 分析每支股票
for i, symbol in enumerate(stock_symbols, 1):
progress_messages.append(f"\n🔍 正在分析 ({i}/{len(stock_symbols)}): {symbol}")
try:
# 獲取股票數據
result = analyzer.fetch_stock_data(symbol.upper())
if len(result) == 3:
success, message, stock_name = result
else:
success, message = result
stock_name = symbol
if not success:
# 記錄錯誤
results.append({
'symbol': symbol,
'name': stock_name or symbol,
'current_price': 'N/A',
'up_probability': 'ERROR',
'down_probability': 'ERROR',
'sideways_probability': 'ERROR',
'confidence': 'ERROR',
'trend_10day_icon': 'N/A',
'trend_10day_desc': '數據不足',
'high_90_icon': 'N/A',
'high_90_desc': '數據不足',
'news_alert': 'N/A',
'volume_alert1': 'N/A',
'institutional_alert1': 'N/A',
'institutional_alert2': 'N/A',
'technical_alert2': 'N/A',
'technical_alert3': 'N/A',
'technical_alert4': 'N/A',
'technical_alert5': 'N/A',
'summary': 'N/A',
'news_message': '',
'volume_alert1_message': '',
'institutional_alert1_message': '',
'institutional_alert2_message': '',
'technical_alert2_message': '',
'technical_alert3_message': '',
'technical_alert4_message': '',
'technical_alert5_message': '',
'summary_message': '',
'analysis_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'error_message': message
})
progress_messages.append(f"❌ {symbol}: {message}")
continue
# 計算技術指標
df = analyzer.calculate_technical_indicators()
if df is None or len(df) < 30:
results.append({
'symbol': symbol,
'name': stock_name or symbol,
'current_price': 'N/A',
'up_probability': 'ERROR',
'down_probability': 'ERROR',
'sideways_probability': 'ERROR',
'confidence': 'ERROR',
'trend_10day_icon': 'N/A',
'trend_10day_desc': '數據不足',
'high_90_icon': 'N/A',
'high_90_desc': '數據不足',
'news_alert': 'N/A',
'volume_alert1': 'N/A',
'institutional_alert1': 'N/A',
'institutional_alert2': 'N/A',
'technical_alert2': 'N/A',
'technical_alert3': 'N/A',
'technical_alert4': 'N/A',
'technical_alert5': 'N/A',
'summary': 'N/A',
'news_message': '',
'volume_alert1_message': '',
'volume_alert2_message': '',
'institutional_alert1_message': '',
'institutional_alert2_message': '',
'technical_alert1_message': '',
'technical_alert2_message': '',
'technical_alert3_message': '',
'technical_alert4_message': '',
'technical_alert5_message': '',
'summary_message': '',
'analysis_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'error_message': '數據不足,無法分析'
})
progress_messages.append(f"❌ {symbol}: 數據不足")
continue
# 獲取新聞情感
news_sentiment = analyzer.get_news_sentiment(symbol)
sentiment_summary = analyzer.analyze_sentiment_summary(news_sentiment)
# 計算預測機率
recent_data = df.tail(20)
technical_signals = []
# 簡化的技術信號計算
latest = df.iloc[-1]
if latest['Close'] > latest['MA20']:
technical_signals.append("價格在20日均線之上")
else:
technical_signals.append("價格在20日均線之下")
probabilities = analyzer.calculate_prediction_probabilities(
technical_signals, sentiment_summary, recent_data
)
# 獲取股票資訊
stock_info = analyzer.get_stock_info(symbol)
# 檢查四項 alert
alerts = analyzer.check_alerts(recent_data, symbol)
# 計算近10日趨勢 - 由 Copilot 生成
trend_icon, trend_desc = analyzer.calculate_10day_trend(df)
# 計算90日高點突破情況 - 由 Copilot 生成
high_90_icon, high_90_desc = analyzer.calculate_90day_high_breakthrough(df)
# 記錄成功結果
results.append({
'symbol': symbol,
'name': stock_info['name'],
'current_price': f"{latest['Close']:.2f}" if latest['Close'] else 'N/A',
'up_probability': f"{probabilities['up']:.1f}",
'down_probability': f"{probabilities['down']:.1f}",
'sideways_probability': f"{probabilities['sideways']:.1f}",
'confidence': f"{probabilities['confidence']*100:.1f}",
'trend_10day_icon': trend_icon,
'trend_10day_desc': trend_desc,
'high_90_icon': high_90_icon,
'high_90_desc': high_90_desc,
'news_alert': alerts['news_alert']['status'],
'volume_alert1': alerts['volume_alert1']['status'],
'institutional_alert1': alerts['institutional_alert1']['status'],
'institutional_alert2': alerts['institutional_alert2']['status'],
'technical_alert2': alerts['technical_alert2']['status'],
'technical_alert3': alerts['technical_alert3']['status'],
'technical_alert4': alerts['technical_alert4']['status'],
'technical_alert5': alerts['technical_alert5']['status'],
'summary': alerts['summary']['status'],
'news_message': alerts['news_alert']['message'],
'volume_alert1_message': alerts['volume_alert1']['message'],
'institutional_alert1_message': alerts['institutional_alert1']['message'],
'institutional_alert2_message': alerts['institutional_alert2']['message'],
'technical_alert2_message': alerts['technical_alert2']['message'],
'technical_alert3_message': alerts['technical_alert3']['message'],
'technical_alert4_message': alerts['technical_alert4']['message'],
'technical_alert5_message': alerts['technical_alert5']['message'],
'summary_message': alerts['summary']['message'],
'analysis_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'error_message': ''
})
progress_messages.append(f"✅ {symbol}: 分析完成")
except Exception as e:
# 處理未預期的錯誤
results.append({
'symbol': symbol,
'name': symbol,
'current_price': 'N/A',
'up_probability': 'ERROR',
'down_probability': 'ERROR',
'sideways_probability': 'ERROR',
'confidence': 'ERROR',
'trend_10day_icon': 'N/A',
'trend_10day_desc': '數據不足',
'high_90_icon': 'N/A',
'high_90_desc': '數據不足',
'news_alert': 'N/A',
'volume_alert1': 'N/A',
'institutional_alert1': 'N/A',
'institutional_alert2': 'N/A',
'technical_alert2': 'N/A',
'technical_alert3': 'N/A',
'technical_alert4': 'N/A',
'technical_alert5': 'N/A',
'summary': 'N/A',
'news_message': '',
'volume_alert1_message': '',
'institutional_alert1_message': '',
'institutional_alert2_message': '',
'technical_alert2_message': '',
'technical_alert3_message': '',
'technical_alert4_message': '',
'technical_alert5_message': '',
'summary_message': '',
'analysis_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'error_message': f'未預期錯誤: {str(e)}'
})
progress_messages.append(f"❌ {symbol}: 未預期錯誤")
# 統計結果
success_count = len([r for r in results if r['error_message'] == ''])
error_count = len(results) - success_count
summary_message = f"""
📈 批次分析完成!
📊 **分析統計:**
- 總計股票數:{len(stock_symbols)}
- 成功分析:{success_count}
- 分析失敗:{error_count}
📊 **圖表已生成:**
- 📊 機率比較柱狀圖
- 🎯 信心度散佈圖
- 📈 綜合評分雷達圖
- 🥧 市場情緒餅圖
🎯 **排序規則:按(上漲機率×信心度)降序排列** - 由 Copilot 生成
🎯 **請查看下方圖表和表格進行投資決策分析!**
"""
progress_log = "\n".join(progress_messages)
# 按(上漲機率最高,信心度最高)排序結果 - 由 Copilot 生成
try:
# 僅對成功分析的股票進行排序
valid_results = [r for r in results if r['error_message'] == '']
error_results = [r for r in results if r['error_message'] != '']
# 對有效結果按上漲機率×信心度排序(降序)
def calculate_sort_key(result):
try:
up_prob = float(result['up_probability']) / 100.0
confidence = float(result['confidence']) / 100.0
return up_prob * confidence
except:
return 0
valid_results.sort(key=calculate_sort_key, reverse=True)
# 重組結果:排序後的有效結果 + 錯誤結果
results = valid_results + error_results
except Exception as e:
print(f"排序過程中發生錯誤: {e}")
# 創建圖表和結果表格
chart_bar, chart_scatter, chart_radar, chart_pie = create_batch_analysis_charts(results)
results_table = create_results_table(results)
return summary_message, progress_log, chart_bar, chart_scatter, chart_radar, chart_pie, results_table
except Exception as e:
return f"❌ 批次分析過程中發生錯誤:{str(e)}", "", None, None, None, None, ""
def smart_recommend_stocks(stock_list_input):
"""智能推薦股票 - 挑選信心度最高的上漲和下跌股票 - 由 Copilot 生成"""
# 檢查輸入是否為空
if not stock_list_input or not stock_list_input.strip():
return "❌ 請輸入股票代碼!可用逗號、空格或換行分隔多個股票代碼。", "", ""
try:
# 解析股票清單(支援多種分隔符)
import re
stock_symbols = re.split(r'[,\s\n]+', stock_list_input.strip())
stock_symbols = [symbol.strip().upper() for symbol in stock_symbols if symbol.strip()]
if not stock_symbols:
return "❌ 未找到有效的股票代碼!", "", ""
# 準備結果列表
results = []
progress_messages = []
progress_messages.append(f"🤖 開始智能推薦分析 {len(stock_symbols)} 支股票...")
# 分析每支股票
for i, symbol in enumerate(stock_symbols, 1):
progress_messages.append(f"\n🔍 正在分析 ({i}/{len(stock_symbols)}): {symbol}")
try:
# 獲取股票數據
result = analyzer.fetch_stock_data(symbol.upper())
if len(result) == 3:
success, message, stock_name = result
else:
success, message = result
stock_name = symbol
if not success:
progress_messages.append(f"❌ {symbol}: {message}")
continue
# 計算技術指標
df = analyzer.calculate_technical_indicators()
if df is None or len(df) < 30:
progress_messages.append(f"❌ {symbol}: 數據不足")
continue
# 獲取新聞情感
news_sentiment = analyzer.get_news_sentiment(symbol)
sentiment_summary = analyzer.analyze_sentiment_summary(news_sentiment)
# 計算預測機率
recent_data = df.tail(20)
technical_signals = []
# 簡化的技術信號計算
latest = df.iloc[-1]
if latest['Close'] > latest['MA20']:
technical_signals.append("價格在20日均線之上")
else:
technical_signals.append("價格在20日均線之下")
probabilities = analyzer.calculate_prediction_probabilities(
technical_signals, sentiment_summary, recent_data
)
# 獲取股票資訊
stock_info = analyzer.get_stock_info(symbol)
# 檢查 Alert
alerts = analyzer.check_alerts(recent_data, symbol)
# 計算近10日趨勢
trend_icon, trend_desc = analyzer.calculate_10day_trend(df)
# 計算90日高點突破情況 - 由 Copilot 生成
high_90_icon, high_90_desc = analyzer.calculate_90day_high_breakthrough(df)
# 計算綜合評分(機率 × 信心度)
recommendation_score = max(probabilities['up'], probabilities['down']) * probabilities['confidence']
# 記錄成功結果
results.append({
'symbol': symbol,
'name': stock_info['name'],
'current_price': f"{latest['Close']:.2f}" if latest['Close'] else 'N/A',
'up_probability': probabilities['up'],
'down_probability': probabilities['down'],
'sideways_probability': probabilities['sideways'],
'confidence': probabilities['confidence'],
'recommendation_score': recommendation_score,
'alerts': alerts,
'trend_icon': trend_icon,
'trend_desc': trend_desc,
'high_90_icon': high_90_icon,
'high_90_desc': high_90_desc,
'analysis_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
})
progress_messages.append(f"✅ {symbol}: 分析完成")
except Exception as e:
progress_messages.append(f"❌ {symbol}: 未預期錯誤")
if not results:
return "❌ 所有股票分析都失敗了,請檢查股票代碼。", "\n".join(progress_messages), ""
# 按上漲信心度和下跌信心度分別排序,取最高的10檔 - 由 Copilot 生成
all_stocks = results.copy()
# 計算上漲信心度和下跌信心度
for stock in all_stocks:
# 上漲信心度 = 上漲機率 × 總信心度 (當上漲機率大於下跌機率時)
if stock['up_probability'] >= stock['down_probability']:
stock['up_confidence_score'] = stock['up_probability'] * stock['confidence']
else:
stock['up_confidence_score'] = 0
# 下跌信心度 = 下跌機率 × 總信心度 (當下跌機率大於上漲機率時)
if stock['down_probability'] >= stock['up_probability']:
stock['down_confidence_score'] = stock['down_probability'] * stock['confidence']
else:
stock['down_confidence_score'] = 0
# 按上漲信心度排序,取前10檔
up_sorted_stocks = [stock for stock in all_stocks if stock['up_confidence_score'] > 0]
up_sorted_stocks.sort(key=lambda x: x['up_confidence_score'], reverse=True)
top_10_up_confidence = up_sorted_stocks[:10]
# 按下跌信心度排序,取前10檔
down_sorted_stocks = [stock for stock in all_stocks if stock['down_confidence_score'] > 0]
down_sorted_stocks.sort(key=lambda x: x['down_confidence_score'], reverse=True)
top_10_down_confidence = down_sorted_stocks[:10]
progress_messages.append(f"\n🎯 推薦完成!")
progress_messages.append(f"🚀 上漲信心度最高:{len(top_10_up_confidence)} 支")
progress_messages.append(f"📉 下跌信心度最高:{len(top_10_down_confidence)} 支")
progress_log = "\n".join(progress_messages)
# 生成推薦報告
recommendation_html = create_recommendation_table(top_10_up_confidence, top_10_down_confidence)
summary_message = f"""
🤖 **智能推薦完成!**
📊 **分析統計:**
- 總計分析:{len(stock_symbols)} 支股票
- 成功分析:{len(results)} 支股票
- 上漲信心度最高:{len(top_10_up_confidence)} 支股票
- 下跌信心度最高:{len(top_10_down_confidence)} 支股票
🎯 **推薦標準:**
- 上漲信心度 = 上漲機率 × 信心度(當上漲機率≥下跌機率時)
- 下跌信心度 = 下跌機率 × 信心度(當下跌機率≥上漲機率時)
- 包含近10日趨勢(漲跌)分析
- 特別標示:量能1、籌碼1、技術2~4皆Y的股票
💡 **下方顯示詳細推薦結果和Alert狀態!**
"""
return summary_message, progress_log, recommendation_html
except Exception as e:
return f"❌ 智能推薦過程中發生錯誤:{str(e)}", "", ""
def create_recommendation_table(up_confidence_stocks, down_confidence_stocks):
"""創建推薦結果表格 - 按上漲信心度和下跌信心度排序 - 由 Copilot 生成"""
def check_special_alert(alerts):
"""檢查是否量能1、籌碼1、技術2~4皆Y - 由 Copilot 生成"""
return (alerts['volume_alert1']['status'] == 'Y' and
alerts['institutional_alert1']['status'] == 'Y' and
alerts['technical_alert2']['status'] == 'Y' and
alerts['technical_alert3']['status'] == 'Y' and
alerts['technical_alert4']['status'] == 'Y')
def generate_prediction_reason(stock):
"""生成預測主要原因 - 由 Copilot 生成"""
alerts = stock['alerts']
up_prob = stock['up_probability']
down_prob = stock['down_probability']
confidence = stock['confidence']
score = stock.get('recommendation_score', 0)
# 統計Y的數量
y_count = sum(1 for key in ['volume_alert1', 'institutional_alert1', 'technical_alert2',
'technical_alert3', 'technical_alert4']
if alerts[key]['status'] == 'Y')
reasons = []
# 根據綜合評分高低分析原因
if score >= 50: # 高評分
if up_prob > down_prob:
reasons.append(f"看多信號強({up_prob:.0f}%>{ down_prob:.0f}%)")
if confidence >= 0.7:
reasons.append(f"高信心度({confidence*100:.0f}%)")
if y_count >= 4:
reasons.append(f"多項指標達標({y_count}/5)")
elif y_count >= 2:
reasons.append(f"部分指標達標({y_count}/5)")
elif score <= 30: # 低評分
if down_prob > up_prob:
reasons.append(f"看空信號({down_prob:.0f}%>{ up_prob:.0f}%)")
if confidence <= 0.4:
reasons.append(f"信心度偏低({confidence*100:.0f}%)")
if y_count <= 1:
reasons.append(f"指標不佳({y_count}/5)")
else:
reasons.append(f"指標混合({y_count}/5)")
else: # 中等評分
if abs(up_prob - down_prob) < 10:
reasons.append("上下機率接近")
if up_prob > down_prob:
reasons.append(f"微偏看多({up_prob:.0f}%)")
else:
reasons.append(f"微偏看空({down_prob:.0f}%)")
reasons.append(f"中等信心度({confidence*100:.0f}%)")
# 特別情況
if check_special_alert(alerts):
reasons.append("⭐全指標達標")
# 趨勢影響
trend_icon = stock.get('trend_icon', '')
if '📈' in trend_icon or 'UP' in trend_icon:
reasons.append("近期走勢上升")
elif '📉' in trend_icon or 'DOWN' in trend_icon:
reasons.append("近期走勢下降")
return ";".join(reasons[:3]) # 最多顯示3個原因
html = """
🤖 AI 智能推薦結果
"""
# 上漲信心度最高表格
if up_confidence_stocks:
html += """
🚀 上漲信心度最高 (Top 10)
| 排名 |
中文名稱 |
股票代號 |
當前價格 |
近10日趨勢 |
90日高點突破 |
上漲機率(%) |
下跌機率(%) |
信心度(%) |
上漲信心度 |
主要原因 |
量能1 |
籌碼1 |
技術2 |
技術3 |
技術4 |
"""
for i, stock in enumerate(up_confidence_stocks, 1):
alerts = stock['alerts']
chinese_name = get_chinese_name(stock['symbol'])
# 檢查特殊標示
is_special = check_special_alert(alerts)
row_style = "background-color: #fff9c4; border: 2px solid #f39c12;" if is_special else "background-color: #f0fff0;"
special_marker = "⭐" if is_special else ""
# Alert 狀態顯示
alert_displays = {}
for alert_key in ['volume_alert1', 'institutional_alert1', 'technical_alert2',
'technical_alert3', 'technical_alert4']:
status = alerts[alert_key]['status']
alert_displays[alert_key] = f"{'🟢' if status == 'Y' else '🔴'} {status}"
# 計算上漲信心度評分和生成主要原因
confidence_score = stock.get('up_confidence_score', 0)
main_reason = generate_prediction_reason(stock)
html += f"""
| #{i} {special_marker} |
{chinese_name} |
{stock['symbol']} |
{stock['current_price']} |
{stock['trend_icon']} {stock['trend_desc']} |
{stock['high_90_icon']} {stock['high_90_desc']} |
{stock['up_probability']:.1f}% |
{stock['down_probability']:.1f}% |
{stock['confidence']*100:.1f}% |
{confidence_score:.1f} |
{main_reason} |
{alert_displays['volume_alert1']} |
{alert_displays['institutional_alert1']} |
{alert_displays['technical_alert2']} |
{alert_displays['technical_alert3']} |
{alert_displays['technical_alert4']} |
"""
html += """
"""
# 下跌信心度最高表格
if down_confidence_stocks:
html += """
📉 下跌信心度最高 (Top 10)
| 排名 |
中文名稱 |
股票代號 |
當前價格 |
近10日趨勢 |
90日高點突破 |
上漲機率(%) |
下跌機率(%) |
信心度(%) |
下跌信心度 |
主要原因 |
量能1 |
籌碼1 |
技術2 |
技術3 |
技術4 |
"""
for i, stock in enumerate(down_confidence_stocks, 1):
alerts = stock['alerts']
chinese_name = get_chinese_name(stock['symbol'])
# 檢查特殊標示
is_special = check_special_alert(alerts)
row_style = "background-color: #fff9c4; border: 2px solid #f39c12;" if is_special else "background-color: #fff0f0;"
special_marker = "⭐" if is_special else ""
# Alert 狀態顯示
alert_displays = {}
for alert_key in ['volume_alert1', 'institutional_alert1', 'technical_alert2',
'technical_alert3', 'technical_alert4']:
status = alerts[alert_key]['status']
alert_displays[alert_key] = f"{'🟢' if status == 'Y' else '🔴'} {status}"
# 計算下跌信心度評分和生成主要原因
confidence_score = stock.get('down_confidence_score', 0)
main_reason = generate_prediction_reason(stock)
html += f"""
| #{i} {special_marker} |
{chinese_name} |
{stock['symbol']} |
{stock['current_price']} |
{stock['trend_icon']} {stock['trend_desc']} |
{stock['high_90_icon']} |
{stock['up_probability']:.1f}% |
{stock['down_probability']:.1f}% |
{stock['confidence']*100:.1f}% |
{confidence_score:.1f} |
{main_reason} |
{alert_displays['volume_alert1']} |
{alert_displays['institutional_alert1']} |
{alert_displays['technical_alert2']} |
{alert_displays['technical_alert3']} |
{alert_displays['technical_alert4']} |
"""
html += """
"""
html += """
📊 智能推薦說明:
• 上漲機率:AI預測股價上漲的可能性百分比
• 下跌機率:AI預測股價下跌的可能性百分比
• 信心度:AI對此預測的信心程度,數值越高越可信
• 信心度評分:上漲信心度 = 上漲機率 × 信心度;下跌信心度 = 下跌機率 × 信心度
• 主要原因:解釋評分高低的關鍵因素和預測依據
• 近10日趨勢:📈↗ 上漲 | 📉↘ 下跌 | ➡️↔ 震盪
• 90日高點突破:🚀 突破90日高點 | 📊 距高點差距百分比
• 特別標示 ⭐:量能1、籌碼1、技術2~4皆為Y的股票
• Alert 燈號:🟢 = 達到條件 (Y) | 🔴 = 未達條件 (N)
註:此推薦僅供參考,請結合個人投資策略謹慎決策 - 由 Copilot 生成
"""
return html
# 創建 Gradio 界面
with gr.Blocks(title="AI 股票分析師", theme=gr.themes.Soft()) as app:
gr.Markdown(
"""
# 📈 AI 股票分析師
### 🤖 使用 Hugging Face 模型進行智能股票分析
**✨ 核心功能:**
- 📊 **完整技術指標**:MA、RSI、MACD、布林通道分析
- 🧠 **AI 情感分析**:使用 FinBERT 模型分析市場情緒
- 🎯 **機率預測**:提供上漲/下跌/盤整機率百分比
- 🚨 **十項 Alert 提醒**:新聞、量能×2、籌碼×2、技術×5、總結 細分警示
- 💼 **買賣建議**:針對持股/未持股提供具體投資建議
- 📈 **智能策略**:根據信心度給出個性化投資策略
- 🎚️ **倉位建議**:提供合理的倉位配置建議
- 🖼️ **互動圖表**:動態視覺化技術指標走勢
- 📁 **批次分析**:一次分析多支股票並匯出詳細報告
**🚀 使用方法:** 單支分析輸入股票代碼,批次分析直接在文本框中輸入多個股票代碼即可!
**💡 新功能亮點:** 現在提供差異化的買賣建議 - 持有股票時建議觀望/賣出/持有,未持有時建議加碼買進/觀望!
"""
)
# 建立分頁
with gr.Tabs():
with gr.TabItem("🎯 單支股票分析"):
with gr.Row():
with gr.Column(scale=1):
stock_input = gr.Textbox(
label="股票代碼",
placeholder="例如:AAPL, TSLA, 2330.TW",
value="2330.TW"
)
analyze_btn = gr.Button("開始分析", variant="primary", size="lg")
status_output = gr.Textbox(
label="分析狀態",
lines=2,
interactive=False
)
with gr.Column(scale=2):
chart_output = gr.Plot(label="股價走勢圖")
prediction_output = gr.Markdown(label="AI 分析報告")
# 事件綁定
analyze_btn.click(
fn=analyze_stock,
inputs=[stock_input],
outputs=[chart_output, status_output, prediction_output]
)
# 範例按鈕
gr.Examples(
examples=[
["AAPL"],
["TSLA"],
["2330.TW"],
["MSFT"],
["GOOGL"]
],
inputs=[stock_input]
)
with gr.TabItem("📊 批次股票分析"):
gr.Markdown(
"""
### 📁 批次分析功能
**📋 使用步驟:**
1. 在下方文本框中輸入股票代碼
2. 支援多種分隔方式:逗號、空格或換行
3. 例如:`AAPL, TSLA, 2330.TW` 或 `AAPL MSFT GOOGL`
4. 點擊「開始批次分析」按鈕查看結果
**📈 輸出內容:**
- 📊 機率比較柱狀圖:直觀對比各股票預測機率
- 🎯 信心度散佈圖:顯示預測可靠性分佈
- 📈 綜合評分雷達圖:多維度股票評分比較
- 🥧 市場情緒餅圖:整體多空情緒統計
- 📈 **近10日趨勢**:顯示股價短期漲跌情況 - 由 Copilot 生成
- 🎯 **智能排序**:按(上漲機率×信心度)降序排列 - 由 Copilot 生成
- 🚨 **十項 Alert 提醒**:新聞、量能×2、籌碼×2、技術×5、總結等全面警示
- �💼 **買賣建議表格**:針對持股與非持股提供具體建議
- 📊 **詳細分析表格**:包含所有機率、信心度與投資建議
- 即時進度顯示和完整錯誤處理
**💡 買賣建議特色:**
- 🏠 **持股建議**:持有、賣出、觀望等具體行動
- 💰 **買進建議**:積極買進、適度買進、暫緩等建議
- 🎚️ **倉位建議**:根據信心度提供合理倉位配置
- ⚠️ **風險警示**:超買超賣等特殊情況提醒
"""
)
# 股票代碼輸入區域
stock_list_input = gr.Textbox(
label="📝 輸入股票代碼",
placeholder="例如:AAPL, TSLA, 2330.TW, MSFT, GOOGL\n或用空格、換行分隔",
lines=3,
value="AAPL, TSLA, 2330.TW, MSFT, GOOGL",
info="支援逗號、空格或換行分隔多個股票代碼"
)
# 範例按鈕
gr.Examples(
examples=[
["AAPL, MSFT, GOOGL, AMZN, TSLA"],
["2330.TW, 2317.TW, 2454.TW, 3711.TW, 2382.TW"],
["JPM, BAC, WFC, C, GS"],
["JNJ, PFE, ABBV, UNH, CVS"],
["BTC-USD, ETH-USD, ^GSPC, ^DJI, ^IXIC"]
],
inputs=[stock_list_input],
label="💡 快速範例"
)
with gr.Row():
batch_analyze_btn = gr.Button(
"🚀 開始批次分析",
variant="primary",
size="lg",
scale=1
)
with gr.Row():
with gr.Column(scale=1):
batch_summary = gr.Markdown(label="📊 分析摘要")
with gr.Column(scale=1):
batch_progress = gr.Textbox(
label="📋 分析進度",
lines=10,
interactive=False,
max_lines=15
)
# 圖表顯示區域
gr.Markdown("## 📈 視覺化分析結果")
with gr.Row():
with gr.Column(scale=1):
chart_probability = gr.Plot(label="📊 股票預測機率比較")
with gr.Column(scale=1):
chart_confidence = gr.Plot(label="🎯 預測信心度分佈")
with gr.Row():
with gr.Column(scale=1):
chart_radar = gr.Plot(label="📈 綜合評分雷達圖")
with gr.Column(scale=1):
chart_sentiment = gr.Plot(label="🥧 整體市場情緒分佈")
# 詳細結果表格
gr.Markdown("## 📋 詳細分析結果")
results_table = gr.HTML(label="分析結果表格")
# 批次分析事件綁定
batch_analyze_btn.click(
fn=batch_analyze_stocks,
inputs=[stock_list_input],
outputs=[
batch_summary,
batch_progress,
chart_probability,
chart_confidence,
chart_radar,
chart_sentiment,
results_table
]
)
with gr.TabItem("🤖 智能推薦"):
gr.Markdown(
"""
### 🤖 AI 智能推薦功能
**🎯 推薦原理:**
- 基於 **綜合評分** = 預測機率 × 信心度
- 自動篩選出上漲和下跌趨勢最明確的股票
- 按評分高低排序,推薦最有潜力的前5名股票
**📊 推薦內容:**
- 📈 **看多推薦**:上漲機率最高且信心度最強的股票
- 📉 **看空推薦**:下跌機率最高且信心度最強的股票
- 🚨 **完整Alert**:顯示所有8項Alert燈號狀態
- 💯 **綜合評分**:客觀量化的推薦強度指標
**💡 使用方式:**
1. 在下方輸入想要分析的股票代碼清單
2. 系統會自動分析所有股票
3. 智能挑選出最值得關注的看多/看空標的
4. 提供詳細的機率、信心度和Alert狀態
**⚡ 特色優勢:**
- 🎯 **精準篩選**:只顯示趨勢明確的高潛力股票
- 📊 **量化排序**:客觀的綜合評分排名系統
- 🚨 **全面警示**:8項Alert完整風險評估
- 💡 **簡化決策**:直接獲得最值得關注的標的
"""
)
# 股票代碼輸入區域
smart_stock_input = gr.Textbox(
label="📝 輸入分析股票代碼",
placeholder="例如:AAPL, TSLA, 2330.TW, MSFT, GOOGL, AMZN, NVDA, META\n或用空格、換行分隔",
lines=4,
value="AAPL, TSLA, 2330.TW, MSFT, GOOGL, AMZN, NVDA, META, 2317.TW, 2454.TW",
info="建議輸入10-20支股票以獲得更好的推薦效果"
)
# 範例按鈕
gr.Examples(
examples=[
["AAPL, MSFT, GOOGL, AMZN, TSLA, NVDA, META, NFLX, AMD, INTC"],
["2330.TW, 2317.TW, 2454.TW, 2382.TW, 2303.TW, 2881.TW, 2883.TW, 2886.TW, 2891.TW, 2892.TW"],
["JPM, BAC, WFC, C, GS, MS, BLK, AXP, COF, SCHW"],
["JNJ, PFE, ABBV, UNH, CVS, MRK, BMY, GILD, AMGN, LLY"],
["BTC-USD, ETH-USD, ^GSPC, ^DJI, ^IXIC, GLD, TLT, VTI, QQQ, SPY"],
["BABA, JD, PDD, BIDU, NIO, XPEV, LI, TME, BILI, IQ"]
],
inputs=[smart_stock_input],
label="💡 快速範例(不同類型股票組合)"
)
with gr.Row():
smart_recommend_btn = gr.Button(
"🤖 開始智能推薦",
variant="primary",
size="lg",
scale=1
)
with gr.Row():
with gr.Column(scale=1):
smart_summary = gr.Markdown(label="📊 推薦摘要")
with gr.Column(scale=1):
smart_progress = gr.Textbox(
label="📋 分析進度",
lines=10,
interactive=False,
max_lines=15
)
# 推薦結果顯示區域
gr.Markdown("## 🏆 智能推薦結果")
smart_results_table = gr.HTML(label="智能推薦表格")
# 智能推薦事件綁定
smart_recommend_btn.click(
fn=smart_recommend_stocks,
inputs=[smart_stock_input],
outputs=[
smart_summary,
smart_progress,
smart_results_table
]
)
# 啟動應用
if __name__ == "__main__":
print("正在啟動 AI 股票分析師...")
# 簡化的啟動邏輯
try:
if IS_HUGGINGFACE_SPACE:
# Hugging Face Spaces 環境 - 使用預設配置
print("在 Hugging Face Spaces 中啟動...")
app.launch()
else:
# 本地環境 - 嘗試多個端口
print("在本地環境中啟動...")
ports_to_try = [7860, 7861, 7862, 7863, 7864, 7865]
launched = False
for port in ports_to_try:
try:
print(f"嘗試端口 {port}...")
app.launch(
share=True,
server_name="0.0.0.0",
server_port=port,
show_error=True,
quiet=False
)
launched = True
break
except OSError as e:
if "port" in str(e).lower():
print(f"端口 {port} 不可用,嘗試下一個...")
continue
else:
raise e
if not launched:
print("所有預設端口都被佔用,使用隨機端口...")
app.launch(
share=True,
server_name="0.0.0.0",
server_port=0, # 0 表示自動分配端口
show_error=True
)
except Exception as e:
print(f"啟動失敗: {e}")
print("請檢查端口使用情況或嘗試重新啟動")
raise e