AlanRex commited on
Commit
23a0c62
·
1 Parent(s): 0d25864

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +55 -34
app.py CHANGED
@@ -16,6 +16,7 @@ from plotly.subplots import make_subplots
16
 
17
  # 台股代號對應表 (移除台指期,因為它現在是獨立區塊)
18
  TAIWAN_STOCKS = {
 
19
  '台積電': '2330.TW',
20
  '聯發科': '2454.TW',
21
  '鴻海': '2317.TW',
@@ -25,20 +26,20 @@ TAIWAN_STOCKS = {
25
  '國泰金': '2882.TW',
26
  '台達電': '2308.TW',
27
  '統一': '1216.TW',
28
- '日月光': '3711.TW',
29
- '長榮': '2603.TW',
30
  '慧洋-KY': '2637.TW',
31
  '上銀': '2049.TW',
32
  '台泥': '1101.TW',
33
  '譜瑞-KY': '4966.TWO',
34
  '貿聯-KY': '3665.TW',
35
- '南電': '8046.TW',
36
  '騰雲': '6870.TWO',
37
  '穩懋': '3105.TWO'
38
  }
39
 
40
  # 產業分類
41
  INDUSTRY_MAPPING = {
 
42
  '2330.TW': '半導體',
43
  '2454.TW': '半導體',
44
  '2317.TW': '電子組件',
@@ -48,14 +49,13 @@ INDUSTRY_MAPPING = {
48
  '2882.TW': '金融',
49
  '2308.TW': '電子',
50
  '1216.TW': '食品',
51
- '3711.TW': '半導體',
52
- '2603.TW': '航運',
53
  '2637.TW': '散裝航運',
54
  '2049.TW': '工具機',
55
  '1101.TW': '營建',
56
  '4966.TWO': '高速傳輸',
57
  '3665.TW': '連接器',
58
- '8046.TW': 'ABF載板',
59
  '6870.TWO': '軟體整合',
60
  '3105.TWO': 'PA功率'
61
  }
@@ -293,7 +293,7 @@ app.layout = html.Div([
293
  # 台指期獨立預測區塊 - 置於頂部
294
  html.Div([
295
  html.H2("🤖 AI深度學習預測 - 台指期指數", style={
296
- 'text-align': 'center',
297
  'color': '#FFCC22',
298
  'margin-bottom': '25px'
299
  }),
@@ -383,7 +383,7 @@ app.layout = html.Div([
383
  ])
384
  ], style={'width': '100%', 'display': 'inline-block', 'vertical-align': 'top'}),
385
  ]),
386
-
387
  # 技術指標選擇區域
388
  html.Div([
389
  html.H3("📊 進階技術指標分析", style={'margin-bottom': '20px'}),
@@ -494,7 +494,9 @@ app.layout = html.Div([
494
  ])
495
  ], style={'margin-top': '30px'}),
496
 
497
- # 多檔股票比較區域
 
 
498
  html.Div([
499
  html.H3("📊 多檔股票比較分析", style={'margin-bottom': '20px'}),
500
  html.Div([
@@ -503,9 +505,14 @@ app.layout = html.Div([
503
  dcc.Dropdown(
504
  id='comparison-stocks',
505
  options=[{'label': name, 'value': symbol} for name, symbol in TAIWAN_STOCKS.items()],
506
- value=['2330.TW', '2454.TW', '2317.TW'], # 預設選擇
507
  multi=True,
508
- style={'margin-bottom': '15px'}
 
 
 
 
 
509
  )
510
  ], style={'width': '60%', 'display': 'inline-block'}),
511
 
@@ -521,7 +528,7 @@ app.layout = html.Div([
521
  ],
522
  value='3mo'
523
  )
524
- ], style={'width': '35%', 'display': 'inline-block', 'margin-left': '5%'})
525
  ]),
526
 
527
  html.Div([
@@ -774,14 +781,14 @@ def update_price_chart(selected_stock, period, chart_type):
774
 
775
  # 添加移動平均線到左側子圖
776
  fig.add_trace(go.Scatter(
777
- x=data.index, y=data['MA5'], mode='lines',
778
  name='MA5', line=dict(color='orange')
779
  ), row=1, col=1)
780
  fig.add_trace(go.Scatter(
781
- x=data.index, y=data['MA20'], mode='lines',
782
  name='MA20', line=dict(color='blue')
783
  ), row=1, col=1)
784
-
785
  # --- 3. 在右側子圖 (col=2) 繪製成交量分佈圖 ---
786
  # 計算 Volume Profile 數據
787
  bin_edges, volume_per_bin, price_centers = calculate_volume_profile(data, num_bins=50)
@@ -806,7 +813,7 @@ def update_price_chart(selected_stock, period, chart_type):
806
  title_text=f'{stock_name} 股價走勢與成交量分佈',
807
  height=500,
808
  showlegend=True,
809
-
810
  # 左側子圖的座標軸設定
811
  xaxis1=dict(
812
  title='日期',
@@ -825,7 +832,7 @@ def update_price_chart(selected_stock, period, chart_type):
825
  yaxis2=dict(
826
  showticklabels=False # 因為共享Y軸,所以隱藏右側的Y軸標籤
827
  ),
828
-
829
  bargap=0.05 # 長條圖間的間隙
830
  )
831
 
@@ -854,7 +861,7 @@ def update_advanced_technical_chart(indicator, selected_stock, period):
854
  fig.add_hline(y=70, line_dash="dash", line_color="green", annotation_text="超買線(70)")
855
  fig.add_hline(y=30, line_dash="dash", line_color="red", annotation_text="超賣線(30)")
856
  fig.add_hline(y=50, line_dash="dot", line_color="gray", annotation_text="中線(50)")
857
-
858
  # 根據台股慣例修改顏色
859
  fig.add_hrect(y0=70, y1=100, fillcolor="green", opacity=0.1)
860
  fig.add_hrect(y0=0, y1=30, fillcolor="red", opacity=0.1)
@@ -876,9 +883,9 @@ def update_advanced_technical_chart(indicator, selected_stock, period):
876
 
877
  # --- 上方子圖 (row=1):只繪製價格走勢 ---
878
  fig.add_trace(go.Scatter(
879
- x=data.index,
880
- y=data['Close'],
881
- mode='lines',
882
  name='收盤價',
883
  line=dict(color='black', width=1.5)
884
  ), row=1, col=1)
@@ -886,28 +893,28 @@ def update_advanced_technical_chart(indicator, selected_stock, period):
886
  # --- 下方子圖 (row=2):繪製所有MACD相關指標 ---
887
  # 1. MACD 快線 (DIF)
888
  fig.add_trace(go.Scatter(
889
- x=data.index,
890
- y=data['MACD'],
891
- mode='lines',
892
  name='MACD (快線)',
893
  line=dict(color='blue', width=2)
894
  ), row=2, col=1)
895
-
896
  # 2. Signal 慢線 (MACD)
897
  fig.add_trace(go.Scatter(
898
- x=data.index,
899
- y=data['MACD_Signal'],
900
- mode='lines',
901
  name='Signal (慢線)',
902
  line=dict(color='red', width=2)
903
  ), row=2, col=1)
904
-
905
  # 3. Histogram 柱狀圖
906
  # 根據台股慣例修改顏色
907
  colors = ['red' if x >= 0 else 'green' for x in data['MACD_Histogram']]
908
  fig.add_trace(go.Bar(
909
- x=data.index,
910
- y=data['MACD_Histogram'],
911
  name='MACD柱狀圖',
912
  marker_color=colors
913
  ), row=2, col=1)
@@ -1039,7 +1046,7 @@ def update_volume_chart(selected_stock, period):
1039
 
1040
  # 根據漲跌決定顏色 (台股慣例)
1041
  colors = ['red' if data['Close'].iloc[i] > data['Open'].iloc[i] else 'green' for i in range(len(data))]
1042
-
1043
  fig = go.Figure()
1044
  fig.add_trace(go.Bar(
1045
  x=data.index,
@@ -1383,7 +1390,10 @@ def update_pmi_chart(selected_stock):
1383
 
1384
  return fig
1385
 
1386
- # 新增:多檔股票比較
 
 
 
1387
  @app.callback(
1388
  [dash.dependencies.Output('comparison-chart', 'figure'),
1389
  dash.dependencies.Output('comparison-table', 'children')],
@@ -1391,6 +1401,16 @@ def update_pmi_chart(selected_stock):
1391
  dash.dependencies.Input('comparison-period', 'value')]
1392
  )
1393
  def update_comparison_analysis(selected_stocks, period):
 
 
 
 
 
 
 
 
 
 
1394
  if not selected_stocks:
1395
  return {}, html.Div("請選擇要比較的股票")
1396
 
@@ -1403,7 +1423,8 @@ def update_comparison_analysis(selected_stocks, period):
1403
  for stock in selected_stocks:
1404
  data = get_stock_data(stock, period)
1405
  if not data.empty:
1406
- stock_name = [name for name, symbol in TAIWAN_STOCKS.items() if symbol == stock][0]
 
1407
 
1408
  # 正規化價格(以期初為基準100)
1409
  normalized_prices = (data['Close'] / data['Close'].iloc[0]) * 100
 
16
 
17
  # 台股代號對應表 (移除台指期,因為它現在是獨立區塊)
18
  TAIWAN_STOCKS = {
19
+ '元大台灣50': '0050.TW', # 新增
20
  '台積電': '2330.TW',
21
  '聯發科': '2454.TW',
22
  '鴻海': '2317.TW',
 
26
  '國泰金': '2882.TW',
27
  '台達電': '2308.TW',
28
  '統一': '1216.TW',
29
+ '日月光': '2311.TW',
30
+ '長榮': '2306.TW',
31
  '慧洋-KY': '2637.TW',
32
  '上銀': '2049.TW',
33
  '台泥': '1101.TW',
34
  '譜瑞-KY': '4966.TWO',
35
  '貿聯-KY': '3665.TW',
 
36
  '騰雲': '6870.TWO',
37
  '穩懋': '3105.TWO'
38
  }
39
 
40
  # 產業分類
41
  INDUSTRY_MAPPING = {
42
+ '0050.TW': 'ETF', # 新增
43
  '2330.TW': '半導體',
44
  '2454.TW': '半導體',
45
  '2317.TW': '電子組件',
 
49
  '2882.TW': '金融',
50
  '2308.TW': '電子',
51
  '1216.TW': '食品',
52
+ '2311.TW': '半導體',
53
+ '2306.TW': '航運',
54
  '2637.TW': '散裝航運',
55
  '2049.TW': '工具機',
56
  '1101.TW': '營建',
57
  '4966.TWO': '高速傳輸',
58
  '3665.TW': '連接器',
 
59
  '6870.TWO': '軟體整合',
60
  '3105.TWO': 'PA功率'
61
  }
 
293
  # 台指期獨立預測區塊 - 置於頂部
294
  html.Div([
295
  html.H2("🤖 AI深度學習預測 - 台指期指數", style={
296
+ 'text-align': 'center',
297
  'color': '#FFCC22',
298
  'margin-bottom': '25px'
299
  }),
 
383
  ])
384
  ], style={'width': '100%', 'display': 'inline-block', 'vertical-align': 'top'}),
385
  ]),
386
+
387
  # 技術指標選擇區域
388
  html.Div([
389
  html.H3("📊 進階技術指標分析", style={'margin-bottom': '20px'}),
 
494
  ])
495
  ], style={'margin-top': '30px'}),
496
 
497
+ # ==============================================================================
498
+ # ===== 修改後的多檔股票比較區域 =====
499
+ # ==============================================================================
500
  html.Div([
501
  html.H3("📊 多檔股票比較分析", style={'margin-bottom': '20px'}),
502
  html.Div([
 
505
  dcc.Dropdown(
506
  id='comparison-stocks',
507
  options=[{'label': name, 'value': symbol} for name, symbol in TAIWAN_STOCKS.items()],
508
+ value=['0050.TW', '2330.TW', '2454.TW'], # 修改:預設包含0050
509
  multi=True,
510
+ style={'margin-bottom': '5px'} # 調整間距
511
+ ),
512
+ # 新增:提示文字
513
+ html.Small(
514
+ '(元大台灣50 (0050.TW) 為固定比較基準,不可移除)',
515
+ style={'display': 'block', 'font-style': 'italic', 'color': 'gray'}
516
  )
517
  ], style={'width': '60%', 'display': 'inline-block'}),
518
 
 
528
  ],
529
  value='3mo'
530
  )
531
+ ], style={'width': '35%', 'display': 'inline-block', 'margin-left': '5%', 'vertical-align': 'top'})
532
  ]),
533
 
534
  html.Div([
 
781
 
782
  # 添加移動平均線到左側子圖
783
  fig.add_trace(go.Scatter(
784
+ x=data.index, y=data['MA5'], mode='lines',
785
  name='MA5', line=dict(color='orange')
786
  ), row=1, col=1)
787
  fig.add_trace(go.Scatter(
788
+ x=data.index, y=data['MA20'], mode='lines',
789
  name='MA20', line=dict(color='blue')
790
  ), row=1, col=1)
791
+
792
  # --- 3. 在右側子圖 (col=2) 繪製成交量分佈圖 ---
793
  # 計算 Volume Profile 數據
794
  bin_edges, volume_per_bin, price_centers = calculate_volume_profile(data, num_bins=50)
 
813
  title_text=f'{stock_name} 股價走勢與成交量分佈',
814
  height=500,
815
  showlegend=True,
816
+
817
  # 左側子圖的座標軸設定
818
  xaxis1=dict(
819
  title='日期',
 
832
  yaxis2=dict(
833
  showticklabels=False # 因為共享Y軸,所以隱藏右側的Y軸標籤
834
  ),
835
+
836
  bargap=0.05 # 長條圖間的間隙
837
  )
838
 
 
861
  fig.add_hline(y=70, line_dash="dash", line_color="green", annotation_text="超買線(70)")
862
  fig.add_hline(y=30, line_dash="dash", line_color="red", annotation_text="超賣線(30)")
863
  fig.add_hline(y=50, line_dash="dot", line_color="gray", annotation_text="中線(50)")
864
+
865
  # 根據台股慣例修改顏色
866
  fig.add_hrect(y0=70, y1=100, fillcolor="green", opacity=0.1)
867
  fig.add_hrect(y0=0, y1=30, fillcolor="red", opacity=0.1)
 
883
 
884
  # --- 上方子圖 (row=1):只繪製價格走勢 ---
885
  fig.add_trace(go.Scatter(
886
+ x=data.index,
887
+ y=data['Close'],
888
+ mode='lines',
889
  name='收盤價',
890
  line=dict(color='black', width=1.5)
891
  ), row=1, col=1)
 
893
  # --- 下方子圖 (row=2):繪製所有MACD相關指標 ---
894
  # 1. MACD 快線 (DIF)
895
  fig.add_trace(go.Scatter(
896
+ x=data.index,
897
+ y=data['MACD'],
898
+ mode='lines',
899
  name='MACD (快線)',
900
  line=dict(color='blue', width=2)
901
  ), row=2, col=1)
902
+
903
  # 2. Signal 慢線 (MACD)
904
  fig.add_trace(go.Scatter(
905
+ x=data.index,
906
+ y=data['MACD_Signal'],
907
+ mode='lines',
908
  name='Signal (慢線)',
909
  line=dict(color='red', width=2)
910
  ), row=2, col=1)
911
+
912
  # 3. Histogram 柱狀圖
913
  # 根據台股慣例修改顏色
914
  colors = ['red' if x >= 0 else 'green' for x in data['MACD_Histogram']]
915
  fig.add_trace(go.Bar(
916
+ x=data.index,
917
+ y=data['MACD_Histogram'],
918
  name='MACD柱狀圖',
919
  marker_color=colors
920
  ), row=2, col=1)
 
1046
 
1047
  # 根據漲跌決定顏色 (台股慣例)
1048
  colors = ['red' if data['Close'].iloc[i] > data['Open'].iloc[i] else 'green' for i in range(len(data))]
1049
+
1050
  fig = go.Figure()
1051
  fig.add_trace(go.Bar(
1052
  x=data.index,
 
1390
 
1391
  return fig
1392
 
1393
+
1394
+ # ==============================================================================
1395
+ # ===== 修改後的多檔股票比較回呼函式 =====
1396
+ # ==============================================================================
1397
  @app.callback(
1398
  [dash.dependencies.Output('comparison-chart', 'figure'),
1399
  dash.dependencies.Output('comparison-table', 'children')],
 
1401
  dash.dependencies.Input('comparison-period', 'value')]
1402
  )
1403
  def update_comparison_analysis(selected_stocks, period):
1404
+ # --- 新增:確保 0050.TW 始終存在 ---
1405
+ fixed_stock = '0050.TW'
1406
+ # 如果列表為空或 None,則只顯示 0050
1407
+ if not selected_stocks:
1408
+ selected_stocks = [fixed_stock]
1409
+ # 如果 0050 不在列表中,則將其插入到最前面
1410
+ elif fixed_stock not in selected_stocks:
1411
+ selected_stocks.insert(0, fixed_stock)
1412
+ # --- 修改結束 ---
1413
+
1414
  if not selected_stocks:
1415
  return {}, html.Div("請選擇要比較的股票")
1416
 
 
1423
  for stock in selected_stocks:
1424
  data = get_stock_data(stock, period)
1425
  if not data.empty:
1426
+ # 安全地獲取股票名稱,如果找不到則使用代碼本身
1427
+ stock_name = next((name for name, symbol in TAIWAN_STOCKS.items() if symbol == stock), stock)
1428
 
1429
  # 正規化價格(以期初為基準100)
1430
  normalized_prices = (data['Close'] / data['Close'].iloc[0]) * 100