Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -14,14 +14,8 @@ import plotly.express as px
|
|
| 14 |
import plotly.graph_objects as go
|
| 15 |
from plotly.subplots import make_subplots
|
| 16 |
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
app = dash.Dash(__name__, suppress_callback_exceptions=True)
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
# 台股代號對應表
|
| 23 |
TAIWAN_STOCKS = {
|
| 24 |
-
'台指期': 'TXF=F', # 台指期貨
|
| 25 |
'台積電': '2330.TW',
|
| 26 |
'聯發科': '2454.TW',
|
| 27 |
'鴻海': '2317.TW',
|
|
@@ -34,10 +28,10 @@ TAIWAN_STOCKS = {
|
|
| 34 |
'日月光': '2311.TW',
|
| 35 |
'長榮': '2306.TW',
|
| 36 |
'慧洋-KY': '2637.TW',
|
| 37 |
-
'上銀'
|
| 38 |
-
'台泥'
|
| 39 |
-
'譜瑞-KY'
|
| 40 |
-
'貿聯-KY'
|
| 41 |
}
|
| 42 |
|
| 43 |
# 產業分類
|
|
@@ -54,10 +48,10 @@ INDUSTRY_MAPPING = {
|
|
| 54 |
'2311.TW': '半導體',
|
| 55 |
'2306.TW': '航運',
|
| 56 |
'2637.TW': '散裝航運',
|
| 57 |
-
'2049.TW'
|
| 58 |
-
'1101.TW'
|
| 59 |
-
'4966.TW'
|
| 60 |
-
'3665.TW'
|
| 61 |
}
|
| 62 |
|
| 63 |
def get_stock_data(symbol, period='1y'):
|
|
@@ -249,14 +243,52 @@ app = dash.Dash(__name__, suppress_callback_exceptions=True)
|
|
| 249 |
app.layout = html.Div([
|
| 250 |
html.H1("台股分析儀表板", style={'text-align': 'center', 'margin-bottom': '30px'}),
|
| 251 |
|
| 252 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
html.Div([
|
| 254 |
html.Div([
|
| 255 |
html.Label("選擇股票:"),
|
| 256 |
dcc.Dropdown(
|
| 257 |
id='stock-dropdown',
|
| 258 |
options=[{'label': name, 'value': symbol} for name, symbol in TAIWAN_STOCKS.items()],
|
| 259 |
-
value='
|
| 260 |
style={'margin-bottom': '10px'}
|
| 261 |
)
|
| 262 |
], style={'width': '30%', 'display': 'inline-block', 'vertical-align': 'top'}),
|
|
@@ -294,9 +326,6 @@ app.layout = html.Div([
|
|
| 294 |
# 股價資訊卡片
|
| 295 |
html.Div(id='stock-info-cards', style={'margin-bottom': '30px'}),
|
| 296 |
|
| 297 |
-
# AI預測區塊 (只在選擇台指期時顯示)
|
| 298 |
-
html.Div(id='prediction-section', style={'margin-bottom': '30px'}),
|
| 299 |
-
|
| 300 |
# 主要圖表區域
|
| 301 |
html.Div([
|
| 302 |
# 左側:股價走勢圖和技術指標
|
|
@@ -503,59 +532,13 @@ app.layout = html.Div([
|
|
| 503 |
})
|
| 504 |
])
|
| 505 |
|
| 506 |
-
#
|
| 507 |
@app.callback(
|
| 508 |
-
dash.dependencies.Output('prediction-
|
| 509 |
-
|
| 510 |
-
|
| 511 |
)
|
| 512 |
-
def
|
| 513 |
-
if selected_stock != 'TXF=F':
|
| 514 |
-
return html.Div() # 只在選擇台指期時顯示預測功能
|
| 515 |
-
|
| 516 |
-
return html.Div([
|
| 517 |
-
html.H3("🤖 AI深度學習預測 - 台指期指數", style={'text-align': 'center', 'color': '#FFCC22'}),
|
| 518 |
-
html.Div([
|
| 519 |
-
html.Div([
|
| 520 |
-
html.Label("預測期間:", style={'font-weight': 'bold', 'color': '#FFCC22'}),
|
| 521 |
-
dcc.Dropdown(
|
| 522 |
-
id='prediction-period',
|
| 523 |
-
options=[
|
| 524 |
-
{'label': '5日後預測', 'value': 5},
|
| 525 |
-
{'label': '10日後預測', 'value': 10},
|
| 526 |
-
{'label': '20日後預測', 'value': 20},
|
| 527 |
-
{'label': '60日後預測', 'value': 60}
|
| 528 |
-
],
|
| 529 |
-
value=5,
|
| 530 |
-
style={'margin-bottom': '10px', 'color': '#272727'}
|
| 531 |
-
)
|
| 532 |
-
], style={'width': '30%', 'display': 'inline-block'}),
|
| 533 |
-
|
| 534 |
-
html.Div(id='prediction-results', style={'width': '65%', 'display': 'inline-block', 'margin-left': '5%'})
|
| 535 |
-
]),
|
| 536 |
-
|
| 537 |
-
html.Div([
|
| 538 |
-
dcc.Graph(id='prediction-chart')
|
| 539 |
-
], style={'margin-top': '20px'})
|
| 540 |
-
], style={
|
| 541 |
-
'background': 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
| 542 |
-
'padding': '25px',
|
| 543 |
-
'border-radius': '15px',
|
| 544 |
-
'box-shadow': '0 8px 25px rgba(0,0,0,0.15)',
|
| 545 |
-
'color': 'white'
|
| 546 |
-
})
|
| 547 |
-
|
| 548 |
-
# 更新預測結果
|
| 549 |
-
@app.callback(
|
| 550 |
-
[dash.dependencies.Output('prediction-results', 'children'),
|
| 551 |
-
dash.dependencies.Output('prediction-chart', 'figure')],
|
| 552 |
-
[dash.dependencies.Input('prediction-period', 'value'),
|
| 553 |
-
dash.dependencies.Input('stock-dropdown', 'value')]
|
| 554 |
-
)
|
| 555 |
-
def update_prediction(predict_days, selected_stock):
|
| 556 |
-
if selected_stock != 'TXF=F':
|
| 557 |
-
return html.Div(), {}
|
| 558 |
-
|
| 559 |
# 獲取台指期歷史資料
|
| 560 |
data = get_stock_data('^TWII', '2y')
|
| 561 |
if data.empty:
|
|
@@ -720,7 +703,7 @@ def update_price_chart(selected_stock, period, chart_type):
|
|
| 720 |
|
| 721 |
return fig
|
| 722 |
|
| 723 |
-
|
| 724 |
@app.callback(
|
| 725 |
dash.dependencies.Output('rsi-chart', 'figure'),
|
| 726 |
[dash.dependencies.Input('stock-dropdown', 'value'),
|
|
@@ -753,8 +736,6 @@ def update_rsi_chart(selected_stock, period):
|
|
| 753 |
|
| 754 |
return fig
|
| 755 |
|
| 756 |
-
|
| 757 |
-
|
| 758 |
# 新增:進階技術指標圖表
|
| 759 |
@app.callback(
|
| 760 |
dash.dependencies.Output('advanced-technical-chart', 'figure'),
|
|
@@ -1137,39 +1118,23 @@ def update_analysis_text(selected_stock, period):
|
|
| 1137 |
|
| 1138 |
# 基本面分析
|
| 1139 |
industry = INDUSTRY_MAPPING.get(selected_stock, '綜合')
|
| 1140 |
-
|
| 1141 |
-
|
| 1142 |
-
html.
|
| 1143 |
-
|
| 1144 |
-
|
| 1145 |
-
|
| 1146 |
-
|
| 1147 |
-
|
| 1148 |
-
|
| 1149 |
-
|
| 1150 |
-
|
| 1151 |
-
|
| 1152 |
-
|
| 1153 |
-
|
| 1154 |
-
|
| 1155 |
-
else:
|
| 1156 |
-
fundamental_text = html.Div([
|
| 1157 |
-
html.P([
|
| 1158 |
-
html.Strong("產業地位:"),
|
| 1159 |
-
f"{stock_name}屬於{industry}產業,在產業鏈中具有",
|
| 1160 |
-
html.Span("重要地位" if selected_stock in ['2330.TW', '2454.TW', '2317.TW'] else "一定影響力",
|
| 1161 |
-
style={'font-weight': 'bold'}),
|
| 1162 |
-
"。"
|
| 1163 |
-
]),
|
| 1164 |
-
html.P([
|
| 1165 |
-
html.Strong("營運展望:"),
|
| 1166 |
-
f"考量{industry}產業前景及公司基本面,建議持續關注季報表現及未來指引。"
|
| 1167 |
-
]),
|
| 1168 |
-
html.P([
|
| 1169 |
-
html.Strong("風險評估:"),
|
| 1170 |
-
"注意產業週期性變化、國際競爭及法規環境變化等風險因子。"
|
| 1171 |
-
])
|
| 1172 |
])
|
|
|
|
| 1173 |
|
| 1174 |
# 市場展望
|
| 1175 |
if price_change > 10:
|
|
@@ -1429,10 +1394,5 @@ if __name__ == '__main__':
|
|
| 1429 |
if not pmi_data.empty:
|
| 1430 |
print(f"PMI資料預覽:\n{pmi_data.head()}")
|
| 1431 |
|
| 1432 |
-
# 在
|
| 1433 |
-
app.
|
| 1434 |
-
|
| 1435 |
-
|
| 1436 |
-
|
| 1437 |
-
# 如果在本地環境執行,使用以下方式
|
| 1438 |
-
# app.run_server(debug=True)
|
|
|
|
| 14 |
import plotly.graph_objects as go
|
| 15 |
from plotly.subplots import make_subplots
|
| 16 |
|
| 17 |
+
# 台股代號對應表 (移除台指期,因為它現在是獨立區塊)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
TAIWAN_STOCKS = {
|
|
|
|
| 19 |
'台積電': '2330.TW',
|
| 20 |
'聯發科': '2454.TW',
|
| 21 |
'鴻海': '2317.TW',
|
|
|
|
| 28 |
'日月光': '2311.TW',
|
| 29 |
'長榮': '2306.TW',
|
| 30 |
'慧洋-KY': '2637.TW',
|
| 31 |
+
'上銀': '2049.TW',
|
| 32 |
+
'台泥': '1101.TW',
|
| 33 |
+
'譜瑞-KY': '4966.TW',
|
| 34 |
+
'貿聯-KY': '3665.TW'
|
| 35 |
}
|
| 36 |
|
| 37 |
# 產業分類
|
|
|
|
| 48 |
'2311.TW': '半導體',
|
| 49 |
'2306.TW': '航運',
|
| 50 |
'2637.TW': '散裝航運',
|
| 51 |
+
'2049.TW': '工具機',
|
| 52 |
+
'1101.TW': '營建',
|
| 53 |
+
'4966.TW': '高速傳輸',
|
| 54 |
+
'3665.TW': '連接器'
|
| 55 |
}
|
| 56 |
|
| 57 |
def get_stock_data(symbol, period='1y'):
|
|
|
|
| 243 |
app.layout = html.Div([
|
| 244 |
html.H1("台股分析儀表板", style={'text-align': 'center', 'margin-bottom': '30px'}),
|
| 245 |
|
| 246 |
+
# 台指期獨立預測區塊 - 置於頂部
|
| 247 |
+
html.Div([
|
| 248 |
+
html.H2("🤖 AI深度學習預測 - 台指期指數", style={
|
| 249 |
+
'text-align': 'center',
|
| 250 |
+
'color': '#FFCC22',
|
| 251 |
+
'margin-bottom': '25px'
|
| 252 |
+
}),
|
| 253 |
+
html.Div([
|
| 254 |
+
html.Div([
|
| 255 |
+
html.Label("預測期間:", style={'font-weight': 'bold', 'color': '#FFCC22'}),
|
| 256 |
+
dcc.Dropdown(
|
| 257 |
+
id='taiex-prediction-period',
|
| 258 |
+
options=[
|
| 259 |
+
{'label': '5日後預測', 'value': 5},
|
| 260 |
+
{'label': '10日後預測', 'value': 10},
|
| 261 |
+
{'label': '20日後預測', 'value': 20},
|
| 262 |
+
{'label': '60日後預測', 'value': 60}
|
| 263 |
+
],
|
| 264 |
+
value=5,
|
| 265 |
+
style={'margin-bottom': '10px', 'color': '#272727'}
|
| 266 |
+
)
|
| 267 |
+
], style={'width': '30%', 'display': 'inline-block'}),
|
| 268 |
+
|
| 269 |
+
html.Div(id='taiex-prediction-results', style={'width': '65%', 'display': 'inline-block', 'margin-left': '5%'})
|
| 270 |
+
]),
|
| 271 |
+
|
| 272 |
+
html.Div([
|
| 273 |
+
dcc.Graph(id='taiex-prediction-chart')
|
| 274 |
+
], style={'margin-top': '20px'})
|
| 275 |
+
], style={
|
| 276 |
+
'background': 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
| 277 |
+
'padding': '25px',
|
| 278 |
+
'border-radius': '15px',
|
| 279 |
+
'box-shadow': '0 8px 25px rgba(0,0,0,0.15)',
|
| 280 |
+
'color': 'white',
|
| 281 |
+
'margin-bottom': '40px'
|
| 282 |
+
}),
|
| 283 |
+
|
| 284 |
+
# 控制面板 (移除台指期選項)
|
| 285 |
html.Div([
|
| 286 |
html.Div([
|
| 287 |
html.Label("選擇股票:"),
|
| 288 |
dcc.Dropdown(
|
| 289 |
id='stock-dropdown',
|
| 290 |
options=[{'label': name, 'value': symbol} for name, symbol in TAIWAN_STOCKS.items()],
|
| 291 |
+
value='2330.TW', # 預設改為台積電
|
| 292 |
style={'margin-bottom': '10px'}
|
| 293 |
)
|
| 294 |
], style={'width': '30%', 'display': 'inline-block', 'vertical-align': 'top'}),
|
|
|
|
| 326 |
# 股價資訊卡片
|
| 327 |
html.Div(id='stock-info-cards', style={'margin-bottom': '30px'}),
|
| 328 |
|
|
|
|
|
|
|
|
|
|
| 329 |
# 主要圖表區域
|
| 330 |
html.Div([
|
| 331 |
# 左側:股價走勢圖和技術指標
|
|
|
|
| 532 |
})
|
| 533 |
])
|
| 534 |
|
| 535 |
+
# 台指期獨立預測回調函數
|
| 536 |
@app.callback(
|
| 537 |
+
[dash.dependencies.Output('taiex-prediction-results', 'children'),
|
| 538 |
+
dash.dependencies.Output('taiex-prediction-chart', 'figure')],
|
| 539 |
+
[dash.dependencies.Input('taiex-prediction-period', 'value')]
|
| 540 |
)
|
| 541 |
+
def update_taiex_prediction(predict_days):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 542 |
# 獲取台指期歷史資料
|
| 543 |
data = get_stock_data('^TWII', '2y')
|
| 544 |
if data.empty:
|
|
|
|
| 703 |
|
| 704 |
return fig
|
| 705 |
|
| 706 |
+
# 更新RSI圖表(保持兼容性)
|
| 707 |
@app.callback(
|
| 708 |
dash.dependencies.Output('rsi-chart', 'figure'),
|
| 709 |
[dash.dependencies.Input('stock-dropdown', 'value'),
|
|
|
|
| 736 |
|
| 737 |
return fig
|
| 738 |
|
|
|
|
|
|
|
| 739 |
# 新增:進階技術指標圖表
|
| 740 |
@app.callback(
|
| 741 |
dash.dependencies.Output('advanced-technical-chart', 'figure'),
|
|
|
|
| 1118 |
|
| 1119 |
# 基本面分析
|
| 1120 |
industry = INDUSTRY_MAPPING.get(selected_stock, '綜合')
|
| 1121 |
+
fundamental_text = html.Div([
|
| 1122 |
+
html.P([
|
| 1123 |
+
html.Strong("產業地位:"),
|
| 1124 |
+
f"{stock_name}屬於{industry}產業,在產業鏈中具有",
|
| 1125 |
+
html.Span("重要地位" if selected_stock in ['2330.TW', '2454.TW', '2317.TW'] else "一定影響力",
|
| 1126 |
+
style={'font-weight': 'bold'}),
|
| 1127 |
+
"。"
|
| 1128 |
+
]),
|
| 1129 |
+
html.P([
|
| 1130 |
+
html.Strong("營運展望:"),
|
| 1131 |
+
f"考量{industry}產業前景及公司基本面,建議持續關注季報表現及未來指引。"
|
| 1132 |
+
]),
|
| 1133 |
+
html.P([
|
| 1134 |
+
html.Strong("風險評估:"),
|
| 1135 |
+
"注意產業週期性變化、國際競爭及法規環境變化等風險因子。"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1136 |
])
|
| 1137 |
+
])
|
| 1138 |
|
| 1139 |
# 市場展望
|
| 1140 |
if price_change > 10:
|
|
|
|
| 1394 |
if not pmi_data.empty:
|
| 1395 |
print(f"PMI資料預覽:\n{pmi_data.head()}")
|
| 1396 |
|
| 1397 |
+
# 在 Hugging Face Spaces 中執行
|
| 1398 |
+
app.run_server(host="0.0.0.0", port=7860, debug=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|