AlanRex commited on
Commit
ed8093d
·
1 Parent(s): 1d1ce22

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +58 -67
app.py CHANGED
@@ -176,76 +176,25 @@ def calculate_technical_indicators(df):
176
  high_max_14 = df['High'].rolling(window=14).max()
177
  df['Williams_R'] = -100 * (high_max_14 - df['Close']) / (high_max_14 - low_min_14)
178
 
179
- return df
 
 
 
 
 
180
 
181
- def get_business_climate_data():
182
- """獲取台灣景氣燈號資料"""
183
- try:
184
- # 檢查檔案是否存在
185
- if not os.path.exists('business_climate.csv'):
186
- print("business_climate.csv 檔案不存在")
187
- return pd.DataFrame()
188
-
189
- # 讀取CSV檔案,假設列名為 Date 和 Index
190
- df = pd.read_csv('business_climate.csv')
191
-
192
- # 檢查列名並調整
193
- if 'Date' not in df.columns:
194
- # 如果第一列是日期,重新命名
195
- df.columns = ['Date', 'Index'] if len(df.columns) == 2 else df.columns
196
-
197
- # 轉換日期格式 (處理 YYYY-MM 格式)
198
- if 'Date' in df.columns:
199
- try:
200
- # 如果是 YYYY-MM 格式,轉換為日期
201
- df['Date'] = pd.to_datetime(df['Date'] + '-01', format='%Y-%m-%d', errors='coerce')
202
- except:
203
- df['Date'] = pd.to_datetime(df['Date'], errors='coerce')
204
-
205
- # 移除日期轉換失敗的行
206
- df = df.dropna(subset=['Date'])
207
-
208
- print(f"成功讀取景氣燈號資料:{len(df)} 筆記錄")
209
- return df
210
 
211
- except Exception as e:
212
- print(f"無法獲取景氣燈號資料: {str(e)}")
213
- return pd.DataFrame()
214
 
215
- def get_pmi_data():
216
- """獲取台灣 PMI 資料"""
217
- try:
218
- # 檢查檔案是否存在
219
- if not os.path.exists('taiwan_pmi.csv'):
220
- print("taiwan_pmi.csv 檔案不存在")
221
- return pd.DataFrame()
222
-
223
- # 讀取CSV檔案
224
- df = pd.read_csv('taiwan_pmi.csv')
225
-
226
- # 檢查列名並調整 (處理 DATE/INDEX 或其他可能的列名)
227
- if 'DATE' in df.columns:
228
- df = df.rename(columns={'DATE': 'Date', 'INDEX': 'Index'})
229
- elif len(df.columns) == 2:
230
- df.columns = ['Date', 'Index']
231
-
232
- # 轉換日期格式
233
- if 'Date' in df.columns:
234
- try:
235
- # 如果是 YYYY-MM 格式,轉換為日期
236
- df['Date'] = pd.to_datetime(df['Date'] + '-01', format='%Y-%m-%d', errors='coerce')
237
- except:
238
- df['Date'] = pd.to_datetime(df['Date'], errors='coerce')
239
-
240
- # 移除日期轉換失敗的行
241
- df = df.dropna(subset=['Date'])
242
-
243
- print(f"成功讀取 PMI 資料:{len(df)} 筆記錄")
244
- return df
245
 
246
- except Exception as e:
247
- print(f"無法獲取 PMI 資料: {str(e)}")
248
- return pd.DataFrame()
249
 
250
  def calculate_volume_profile(df, num_bins=50):
251
  """
@@ -401,7 +350,8 @@ app.layout = html.Div([
401
  {'label': 'MACD 指數平滑異同移動平均線', 'value': 'MACD'},
402
  {'label': '布林通道 Bollinger Bands', 'value': 'BB'},
403
  {'label': 'KD 隨機指標', 'value': 'KD'},
404
- {'label': '威廉指標 %R', 'value': 'WR'}
 
405
  ],
406
  value='RSI',
407
  style={'width': '100%'}
@@ -1034,6 +984,34 @@ def update_advanced_technical_chart(indicator, selected_stock, period):
1034
  )
1035
  fig.update_yaxes(range=[-100, 0], row=2, col=1)
1036
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1037
  return fig
1038
 
1039
  # 更新成交量圖表
@@ -1210,6 +1188,10 @@ def update_analysis_text(selected_stock, period):
1210
  bb_position = data['BB_Position'].iloc[-1] if not pd.isna(data['BB_Position'].iloc[-1]) else 0.5
1211
  k_current = data['K'].iloc[-1] if not pd.isna(data['K'].iloc[-1]) else 50
1212
  d_current = data['D'].iloc[-1] if not pd.isna(data['D'].iloc[-1]) else 50
 
 
 
 
1213
 
1214
  # 技術面分析
1215
  # 根據台股慣例修改顏色
@@ -1264,6 +1246,15 @@ def update_analysis_text(selected_stock, period):
1264
  ),
1265
  "。"
1266
  ]),
 
 
 
 
 
 
 
 
 
1267
  html.P([
1268
  html.Strong("成交量分析:"),
1269
  f"近期成交量{'放大' if recent_volume > volume_avg * 1.2 else '萎縮' if recent_volume < volume_avg * 0.8 else '平穩'},",
 
176
  high_max_14 = df['High'].rolling(window=14).max()
177
  df['Williams_R'] = -100 * (high_max_14 - df['Close']) / (high_max_14 - low_min_14)
178
 
179
+ # DMI (Directional Movement Index)
180
+ # 計算正向運動 (+DM) 和負向運動 (-DM)
181
+ df['up_move'] = df['High'].diff()
182
+ df['down_move'] = df['Low'].diff()
183
+ df['+DM'] = np.where((df['up_move'] > df['down_move']) & (df['up_move'] > 0), df['up_move'], 0)
184
+ df['-DM'] = np.where((df['down_move'] > df['up_move']) & (df['down_move'] > 0), df['down_move'], 0)
185
 
186
+ # 計算真實範圍 (TR)
187
+ df['TR'] = np.max([df['High'] - df['Low'], abs(df['High'] - df['Close'].shift(1)), abs(df['Low'] - df['Close'].shift(1))], axis=0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
 
189
+ # 計算平滑後的 +DM, -DM, TR (通常使用 14 天)
190
+ df['+DI'] = df['+DM'].ewm(alpha=1/14, adjust=False).mean() / df['TR'].ewm(alpha=1/14, adjust=False).mean() * 100
191
+ df['-DI'] = df['-DM'].ewm(alpha=1/14, adjust=False).mean() / df['TR'].ewm(alpha=1/14, adjust=False).mean() * 100
192
 
193
+ # 計算 ADX
194
+ df['DX'] = abs(df['+DI'] - df['-DI']) / (df['+DI'] + df['-DI']) * 100
195
+ df['ADX'] = df['DX'].ewm(alpha=1/14, adjust=False).mean()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
 
197
+ return df
 
 
198
 
199
  def calculate_volume_profile(df, num_bins=50):
200
  """
 
350
  {'label': 'MACD 指數平滑異同移動平均線', 'value': 'MACD'},
351
  {'label': '布林通道 Bollinger Bands', 'value': 'BB'},
352
  {'label': 'KD 隨機指標', 'value': 'KD'},
353
+ {'label': '威廉指標 %R', 'value': 'WR'},
354
+ {'label': 'DMI 動向指標', 'value': 'DMI'}
355
  ],
356
  value='RSI',
357
  style={'width': '100%'}
 
984
  )
985
  fig.update_yaxes(range=[-100, 0], row=2, col=1)
986
 
987
+ elif indicator == 'DMI':
988
+ fig = make_subplots(rows=2, cols=1, shared_xaxes=True,
989
+ vertical_spacing=0.1,
990
+ row_heights=[0.6, 0.4],
991
+ subplot_titles=('價格走勢', 'DMI 指標'))
992
+
993
+ # 上方:價格線
994
+ fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='收盤價',
995
+ line=dict(color='black', width=1)), row=1, col=1)
996
+
997
+ # 下方:DMI 線
998
+ fig.add_trace(go.Scatter(x=data.index, y=data['+DI'], mode='lines', name='+DI',
999
+ line=dict(color='red', width=2)), row=2, col=1)
1000
+ fig.add_trace(go.Scatter(x=data.index, y=data['-DI'], mode='lines', name='-DI',
1001
+ line=dict(color='green', width=2)), row=2, col=1)
1002
+ fig.add_trace(go.Scatter(x=data.index, y=data['ADX'], mode='lines', name='ADX',
1003
+ line=dict(color='blue', width=2, dash='dot')), row=2, col=1)
1004
+
1005
+ # DMI 參考線
1006
+ fig.add_hline(y=20, line_dash="dash", line_color="gray", annotation_text="ADX強弱線(20)", row=2, col=1)
1007
+
1008
+ fig.update_layout(
1009
+ title=f'{stock_name} - DMI 動向指標 (14日)',
1010
+ height=500,
1011
+ showlegend=True
1012
+ )
1013
+ fig.update_yaxes(range=[0, 100], row=2, col=1)
1014
+
1015
  return fig
1016
 
1017
  # 更新成交量圖表
 
1188
  bb_position = data['BB_Position'].iloc[-1] if not pd.isna(data['BB_Position'].iloc[-1]) else 0.5
1189
  k_current = data['K'].iloc[-1] if not pd.isna(data['K'].iloc[-1]) else 50
1190
  d_current = data['D'].iloc[-1] if not pd.isna(data['D'].iloc[-1]) else 50
1191
+ pdi_current = data['+DI'].iloc[-1] if not pd.isna(data['+DI'].iloc[-1]) else 0
1192
+ ndi_current = data['-DI'].iloc[-1] if not pd.isna(data['-DI'].iloc[-1]) else 0
1193
+ adx_current = data['ADX'].iloc[-1] if not pd.isna(data['ADX'].iloc[-1]) else 0
1194
+
1195
 
1196
  # 技術面分析
1197
  # 根據台股慣例修改顏色
 
1246
  ),
1247
  "。"
1248
  ]),
1249
+ html.P([
1250
+ html.Strong("DMI指標:"),
1251
+ f"目前+DI ({pdi_current:.1f}) 與 -DI ({ndi_current:.1f}),",
1252
+ html.Span(
1253
+ "呈現多頭趨勢" if pdi_current > ndi_current else "呈現空頭趨勢",
1254
+ style={'color': 'red' if pdi_current > ndi_current else 'green', 'font-weight': 'bold'}
1255
+ ),
1256
+ f"。ADX值為 {adx_current:.1f},顯示市場趨勢{'強勁' if adx_current > 25 else '不明顯' if adx_current < 20 else '有趨勢'}"
1257
+ ]),
1258
  html.P([
1259
  html.Strong("成交量分析:"),
1260
  f"近期成交量{'放大' if recent_volume > volume_avg * 1.2 else '萎縮' if recent_volume < volume_avg * 0.8 else '平穩'},",