AlanRex commited on
Commit
068e7c7
·
1 Parent(s): abdf9ed

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +86 -127
app.py CHANGED
@@ -374,30 +374,14 @@ app.layout = html.Div([
374
 
375
  # 主要圖表區域 - 移除RSI圖表
376
  html.Div([
377
- # 左側:股價走勢圖
378
  html.Div([
379
  html.Div([
380
  dcc.Graph(id='price-chart')
381
  ])
382
- ], style={'width': '65%', 'display': 'inline-block', 'vertical-align': 'top'}),
383
-
384
- # 右側:分析資訊面板
385
- html.Div([
386
- html.Div(id='analysis-panel')
387
- ], style={'width': '33%', 'display': 'inline-block', 'margin-left': '2%', 'vertical-align': 'top'})
388
- ]),
389
- # 新增:成交量分佈圖 (Volume Profile)
390
- html.Div([
391
- html.H3("📊 成交量分佈圖 (Volume Profile)"),
392
- dcc.Graph(id='volume-profile-chart')
393
- ], style={
394
- 'margin-top': '30px',
395
- 'padding': '20px',
396
- 'background': 'white',
397
- 'border-radius': '10px',
398
- 'box-shadow': '0 2px 10px rgba(0,0,0,0.1)'
399
- }),
400
-
401
  # 技術指標選擇區域
402
  html.Div([
403
  html.H3("📊 進階技術指標分析", style={'margin-bottom': '20px'}),
@@ -744,7 +728,9 @@ def update_stock_info(selected_stock):
744
  })
745
  ])
746
 
747
- # 更新股價圖表
 
 
748
  @app.callback(
749
  dash.dependencies.Output('price-chart', 'figure'),
750
  [dash.dependencies.Input('stock-dropdown', 'value'),
@@ -759,9 +745,19 @@ def update_price_chart(selected_stock, period, chart_type):
759
  data = calculate_technical_indicators(data)
760
  stock_name = [name for name, symbol in TAIWAN_STOCKS.items() if symbol == selected_stock][0]
761
 
 
 
 
 
 
 
 
 
 
 
762
  if chart_type == 'candlestick':
763
  # 根據台股慣例修改顏色
764
- fig = go.Figure(data=go.Candlestick(
765
  x=data.index,
766
  open=data['Open'],
767
  high=data['High'],
@@ -769,58 +765,71 @@ def update_price_chart(selected_stock, period, chart_type):
769
  close=data['Close'],
770
  name=stock_name,
771
  increasing_line_color='red', # 上漲為紅色
772
- decreasing_line_color='green' # 下跌為綠色
773
- ))
774
  else:
775
- fig = px.line(data, y='Close', title=f'{stock_name} 股價走勢')
776
 
777
- # 添加移動平均線
778
- fig.add_trace(go.Scatter(x=data.index, y=data['MA5'], mode='lines', name='MA5', line=dict(color='orange')))
779
- fig.add_trace(go.Scatter(x=data.index, y=data['MA20'], mode='lines', name='MA20', line=dict(color='blue')))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
780
 
 
781
  fig.update_layout(
782
- title=f'{stock_name} 股價走勢',
783
- xaxis_title='日期',
784
- yaxis_title='價格 (TWD)',
785
- height=400
786
- )
787
-
788
- return fig
789
-
790
- # 更新RSI圖表(保持兼容性)
791
- @app.callback(
792
- dash.dependencies.Output('rsi-chart', 'figure'),
793
- [dash.dependencies.Input('stock-dropdown', 'value'),
794
- dash.dependencies.Input('period-dropdown', 'value')]
795
- )
796
- def update_rsi_chart(selected_stock, period):
797
- data = get_stock_data(selected_stock, period)
798
- if data.empty:
799
- return {}
800
-
801
- data = calculate_technical_indicators(data)
802
-
803
- fig = go.Figure()
804
- fig.add_trace(go.Scatter(x=data.index, y=data['RSI'], mode='lines', name='RSI', line=dict(color='purple', width=2)))
805
- # 根據台股慣例修改顏色
806
- fig.add_hline(y=70, line_dash="dash", line_color="green", annotation_text="超買線(70)")
807
- fig.add_hline(y=30, line_dash="dash", line_color="red", annotation_text="超賣線(30)")
808
- fig.add_hline(y=50, line_dash="dot", line_color="gray", annotation_text="中線(50)")
809
-
810
- # 添加超買超賣區域背景
811
- fig.add_hrect(y0=70, y1=100, fillcolor="green", opacity=0.1, annotation_text="超買區")
812
- fig.add_hrect(y0=0, y1=30, fillcolor="red", opacity=0.1, annotation_text="超賣區")
813
 
814
- fig.update_layout(
815
- title='RSI 相對強弱指標',
816
- xaxis_title='日期',
817
- yaxis_title='RSI',
818
- height=400,
819
- yaxis=dict(range=[0, 100])
 
 
 
 
820
  )
821
 
822
  return fig
823
 
 
824
  # 新增:進階技術指標圖表
825
  @app.callback(
826
  dash.dependencies.Output('advanced-technical-chart', 'figure'),
@@ -1026,8 +1035,19 @@ def update_volume_chart(selected_stock, period):
1026
 
1027
  stock_name = [name for name, symbol in TAIWAN_STOCKS.items() if symbol == selected_stock][0]
1028
 
1029
- fig = px.bar(data, y='Volume', title=f'{stock_name} 成交量')
 
 
 
 
 
 
 
 
 
 
1030
  fig.update_layout(
 
1031
  xaxis_title='日期',
1032
  yaxis_title='成交量',
1033
  height=300
@@ -1361,67 +1381,6 @@ def update_pmi_chart(selected_stock):
1361
 
1362
  return fig
1363
 
1364
-
1365
- @app.callback(
1366
- dash.dependencies.Output('volume-profile-chart', 'figure'),
1367
- [dash.dependencies.Input('stock-dropdown', 'value'),
1368
- dash.dependencies.Input('period-dropdown', 'value')]
1369
- )
1370
- def update_volume_profile_chart(selected_stock, period):
1371
- data = get_stock_data(selected_stock, period)
1372
- if data.empty:
1373
- return {}
1374
-
1375
- stock_name = [name for name, symbol in TAIWAN_STOCKS.items() if symbol == selected_stock][0]
1376
-
1377
- # 計算 Volume Profile
1378
- bin_edges, volume_per_bin, price_centers = calculate_volume_profile(data, num_bins=50) # 您可以調整 num_bins
1379
-
1380
- if bin_edges is None or volume_per_bin is None:
1381
- return {}
1382
-
1383
- # 創建 Volume Profile 圖 (通常是水平長條圖)
1384
- # 我們將其繪製為一個水平的長條圖,成交量在 X 軸,價格在 Y 軸
1385
- fig = go.Figure(go.Bar(
1386
- orientation='h', # 設定為水平長條圖
1387
- y=price_centers,
1388
- x=volume_per_bin,
1389
- name='Volume Profile',
1390
- marker=dict(
1391
- color='rgba(173, 216, 230, 0.6)', # 淡藍色
1392
- line=dict(color='rgba(30, 144, 255, 0.8)', width=1) # 邊框線
1393
- ),
1394
- # 顯示具體的成交量數字
1395
- text=[f'{vol:.0f}' for vol in volume_per_bin],
1396
- textposition='outside', # 將文字顯示在長條圖外面
1397
- hoverinfo='y+text' # hover 時顯示 Y 軸 (價格) 和 text (成交量)
1398
- ))
1399
-
1400
- # 獲取最高成交量的價格區間 (Point of Control, POC)
1401
- if len(volume_per_bin) > 0:
1402
- poc_volume = np.max(volume_per_bin)
1403
- poc_index = np.argmax(volume_per_bin)
1404
- poc_price = price_centers[poc_index]
1405
-
1406
- # 在 POC 價格線上添加一條垂直線
1407
- fig.add_vline(x=poc_volume, line_dash="dash", line_color="red",
1408
- annotation_text=f"POC: ${poc_price:.2f} ({poc_volume:.0f})",
1409
- annotation_position="top right")
1410
-
1411
- # 更新圖表佈局
1412
- fig.update_layout(
1413
- title=f'{stock_name} 成交量分佈圖 (Volume Profile)',
1414
- xaxis_title='成交量',
1415
- yaxis_title='價格 (TWD)',
1416
- height=450,
1417
- yaxis=dict(autorange='reversed'), # 讓價格從高到低排列
1418
- bargap=0, # 讓長條圖緊密排列
1419
- plot_bgcolor='rgba(0,0,0,0)', # 透明背景
1420
- hoverlabel=dict(bgcolor="white", font_size=12, font_family="Rockwell")
1421
- )
1422
-
1423
- return fig
1424
-
1425
  # 新增:多檔股票比較
1426
  @app.callback(
1427
  [dash.dependencies.Output('comparison-chart', 'figure'),
 
374
 
375
  # 主要圖表區域 - 移除RSI圖表
376
  html.Div([
377
+ # 左側:股價走勢圖 (現在包含成交量分佈)
378
  html.Div([
379
  html.Div([
380
  dcc.Graph(id='price-chart')
381
  ])
382
+ ], style={'width': '100%', 'display': 'inline-block', 'vertical-align': 'top'}),
383
+ ]),
384
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385
  # 技術指標選擇區域
386
  html.Div([
387
  html.H3("📊 進階技術指標分析", style={'margin-bottom': '20px'}),
 
728
  })
729
  ])
730
 
731
+ # ==============================================================================
732
+ # ===== 修改後的主要圖表回呼函式 (合併股價與成交量分佈) =====
733
+ # ==============================================================================
734
  @app.callback(
735
  dash.dependencies.Output('price-chart', 'figure'),
736
  [dash.dependencies.Input('stock-dropdown', 'value'),
 
745
  data = calculate_technical_indicators(data)
746
  stock_name = [name for name, symbol in TAIWAN_STOCKS.items() if symbol == selected_stock][0]
747
 
748
+ # --- 1. 建立共享 Y 軸的子圖 ---
749
+ # 建立一個 1x2 的網格,設定欄位寬度比例,並共享 Y 軸
750
+ fig = make_subplots(
751
+ rows=1, cols=2,
752
+ shared_yaxes=True,
753
+ column_widths=[0.8, 0.2], # 左側圖佔80%,右側圖佔20%
754
+ horizontal_spacing=0.01 # 子圖間的水平間距
755
+ )
756
+
757
+ # --- 2. 在左側子圖 (col=1) 繪製股價圖 ---
758
  if chart_type == 'candlestick':
759
  # 根據台股慣例修改顏色
760
+ fig.add_trace(go.Candlestick(
761
  x=data.index,
762
  open=data['Open'],
763
  high=data['High'],
 
765
  close=data['Close'],
766
  name=stock_name,
767
  increasing_line_color='red', # 上漲為紅色
768
+ decreasing_line_color='green' # 下跌為綠色
769
+ ), row=1, col=1)
770
  else:
771
+ fig.add_trace(px.line(data, y='Close').data[0], row=1, col=1)
772
 
773
+ # 添加移動平均線到左側子圖
774
+ fig.add_trace(go.Scatter(
775
+ x=data.index, y=data['MA5'], mode='lines',
776
+ name='MA5', line=dict(color='orange')
777
+ ), row=1, col=1)
778
+ fig.add_trace(go.Scatter(
779
+ x=data.index, y=data['MA20'], mode='lines',
780
+ name='MA20', line=dict(color='blue')
781
+ ), row=1, col=1)
782
+
783
+ # --- 3. 在右側子圖 (col=2) 繪製成交量分佈圖 ---
784
+ # 計算 Volume Profile 數據
785
+ bin_edges, volume_per_bin, price_centers = calculate_volume_profile(data, num_bins=50)
786
+
787
+ if volume_per_bin is not None:
788
+ # 繪製水平長條圖
789
+ fig.add_trace(go.Bar(
790
+ orientation='h',
791
+ y=price_centers,
792
+ x=volume_per_bin,
793
+ name='Volume Profile',
794
+ text=[f'{vol/1000:.0f}k' for vol in volume_per_bin], # 顯示成交量
795
+ textposition='auto',
796
+ marker=dict(
797
+ color='rgba(173, 216, 230, 0.6)',
798
+ line=dict(color='rgba(30, 144, 255, 0.8)', width=1)
799
+ )
800
+ ), row=1, col=2)
801
 
802
+ # --- 4. 更新整體圖表佈局 ---
803
  fig.update_layout(
804
+ title_text=f'{stock_name} 股價走勢與成交量分佈',
805
+ height=500,
806
+ showlegend=True,
807
+
808
+ # 左側子圖的座標軸設定
809
+ xaxis1=dict(
810
+ title='日期',
811
+ type='date',
812
+ rangeslider_visible=False # 隱藏範圍滑桿,避免干擾佈局
813
+ ),
814
+ yaxis1=dict(
815
+ title='價格 (TWD)'
816
+ ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
817
 
818
+ # 右側子圖的座標軸設定
819
+ xaxis2=dict(
820
+ title='成交量',
821
+ showticklabels=True # 顯示刻度
822
+ ),
823
+ yaxis2=dict(
824
+ showticklabels=False # 因為共享Y軸,所以隱藏右側的Y軸標籤
825
+ ),
826
+
827
+ bargap=0.05 # 長條圖間的間隙
828
  )
829
 
830
  return fig
831
 
832
+
833
  # 新增:進階技術指標圖表
834
  @app.callback(
835
  dash.dependencies.Output('advanced-technical-chart', 'figure'),
 
1035
 
1036
  stock_name = [name for name, symbol in TAIWAN_STOCKS.items() if symbol == selected_stock][0]
1037
 
1038
+ # 根據漲跌決定顏色 (台股慣例)
1039
+ colors = ['red' if data['Close'].iloc[i] > data['Open'].iloc[i] else 'green' for i in range(len(data))]
1040
+
1041
+ fig = go.Figure()
1042
+ fig.add_trace(go.Bar(
1043
+ x=data.index,
1044
+ y=data['Volume'],
1045
+ marker_color=colors,
1046
+ name='成交量'
1047
+ ))
1048
+
1049
  fig.update_layout(
1050
+ title=f'{stock_name} 成交量',
1051
  xaxis_title='日期',
1052
  yaxis_title='成交量',
1053
  height=300
 
1381
 
1382
  return fig
1383
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1384
  # 新增:多檔股票比較
1385
  @app.callback(
1386
  [dash.dependencies.Output('comparison-chart', 'figure'),