Update app.py
Browse files
app.py
CHANGED
|
@@ -176,10 +176,9 @@ 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 |
-
#
|
| 180 |
-
# 計算正向運動 (+DM) 和負向運動 (-DM)
|
| 181 |
df['up_move'] = df['High'] - df['High'].shift(1)
|
| 182 |
-
df['down_move'] = df['Low'].shift(1) - df['Low']
|
| 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 |
|
|
@@ -199,21 +198,10 @@ def calculate_technical_indicators(df):
|
|
| 199 |
def calculate_volume_profile(df, num_bins=50):
|
| 200 |
"""
|
| 201 |
計算成交量分佈圖 (Volume Profile) 的數據。
|
| 202 |
-
|
| 203 |
-
Args:
|
| 204 |
-
df (pd.DataFrame): 包含 'High', 'Low', 'Volume' 欄位的 DataFrame。
|
| 205 |
-
num_bins (int): 分割價格區間的數量。
|
| 206 |
-
|
| 207 |
-
Returns:
|
| 208 |
-
tuple: 包含 (bin_edges, volume_per_bin, price_centers) 的 tuple。
|
| 209 |
-
bin_edges: 每個區間的邊界。
|
| 210 |
-
volume_per_bin: 每個區間對應的成交量。
|
| 211 |
-
price_centers: 每個區間的中心價格。
|
| 212 |
"""
|
| 213 |
if df.empty or 'High' not in df.columns or 'Low' not in df.columns or 'Volume' not in df.columns:
|
| 214 |
return None, None, None
|
| 215 |
|
| 216 |
-
# 建立一個包含所有高低點的陣列,用於確定價格範圍
|
| 217 |
all_prices = np.concatenate([df['High'].values, df['Low'].values])
|
| 218 |
min_price = all_prices.min()
|
| 219 |
max_price = all_prices.max()
|
|
@@ -221,15 +209,9 @@ def calculate_volume_profile(df, num_bins=50):
|
|
| 221 |
price_for_volume = (df['High'] + df['Low'] + df['Close']) / 3
|
| 222 |
df_vol_profile = df.copy()
|
| 223 |
df_vol_profile['Price_Indicator'] = price_for_volume
|
| 224 |
-
df_vol_profile['Volume'] = df_vol_profile['Volume']
|
| 225 |
|
| 226 |
-
# 創建直方圖來計算成交量分佈
|
| 227 |
-
# `density=False` 確保我們得到的是實際的成交量總和,而不是密度
|
| 228 |
-
# `bins=num_bins` 設定價格區間的數量
|
| 229 |
-
# `range` 設定價格的最小值和最大值
|
| 230 |
hist, bin_edges = np.histogram(df_vol_profile['Price_Indicator'], bins=num_bins, range=(min_price, max_price), weights=df_vol_profile['Volume'])
|
| 231 |
-
|
| 232 |
-
# 計算每個區間的中心價格
|
| 233 |
price_centers = (bin_edges[:-1] + bin_edges[1:]) / 2
|
| 234 |
|
| 235 |
return bin_edges, hist, price_centers
|
|
@@ -1140,7 +1122,6 @@ def update_analysis_text(selected_stock, period):
|
|
| 1140 |
|
| 1141 |
|
| 1142 |
# 技術面分析
|
| 1143 |
-
# 根據台股慣例修改顏色
|
| 1144 |
technical_text = html.Div([
|
| 1145 |
html.P([
|
| 1146 |
html.Strong("價格趨勢:"),
|
|
@@ -1229,7 +1210,6 @@ def update_analysis_text(selected_stock, period):
|
|
| 1229 |
])
|
| 1230 |
|
| 1231 |
# 市場展望
|
| 1232 |
-
# 根據台股慣例修改顏色
|
| 1233 |
if price_change > 10:
|
| 1234 |
outlook_tone = "謹慎樂觀"
|
| 1235 |
outlook_color = "#dc3545"
|
|
@@ -1285,7 +1265,6 @@ def update_pmi_chart(selected_stock):
|
|
| 1285 |
return fig
|
| 1286 |
|
| 1287 |
# 定義PMI顏色 (50以上擴張,以下緊縮)
|
| 1288 |
-
# 根據台股慣例修改顏色
|
| 1289 |
def get_pmi_color(value):
|
| 1290 |
return 'red' if value >= 50 else 'green'
|
| 1291 |
|
|
@@ -1310,7 +1289,6 @@ def update_pmi_chart(selected_stock):
|
|
| 1310 |
fig.add_hline(y=50, line_dash="dash", line_color="black", annotation_text="榮枯線(50)")
|
| 1311 |
|
| 1312 |
# 添加背景色區域
|
| 1313 |
-
# 根據台股慣例修改顏色
|
| 1314 |
fig.add_hrect(
|
| 1315 |
y0=50, y1=60,
|
| 1316 |
fillcolor="lightcoral", opacity=0.2,
|
|
@@ -1402,7 +1380,6 @@ def update_comparison_analysis(selected_stocks, period):
|
|
| 1402 |
if comparison_data:
|
| 1403 |
table_rows = []
|
| 1404 |
for item in sorted(comparison_data, key=lambda x: x['return'], reverse=True):
|
| 1405 |
-
# 根據台股慣例修改顏色
|
| 1406 |
color = 'red' if item['return'] > 0 else 'green'
|
| 1407 |
table_rows.append(
|
| 1408 |
html.Tr([
|
|
@@ -1444,7 +1421,6 @@ def update_sentiment_analysis(selected_stock):
|
|
| 1444 |
sentiment_score = np.random.uniform(30, 80) # 模擬情緒分數 0-100
|
| 1445 |
|
| 1446 |
# 建立情緒指標圓形圖
|
| 1447 |
-
# 根據台股慣例修改顏色
|
| 1448 |
gauge_fig = go.Figure(go.Indicator(
|
| 1449 |
mode = "gauge+number+delta",
|
| 1450 |
value = sentiment_score,
|
|
|
|
| 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 |
df['up_move'] = df['High'] - df['High'].shift(1)
|
| 181 |
+
df['down_move'] = df['Low'].shift(1) - df['Low']
|
| 182 |
df['+DM'] = np.where((df['up_move'] > df['down_move']) & (df['up_move'] > 0), df['up_move'], 0)
|
| 183 |
df['-DM'] = np.where((df['down_move'] > df['up_move']) & (df['down_move'] > 0), df['down_move'], 0)
|
| 184 |
|
|
|
|
| 198 |
def calculate_volume_profile(df, num_bins=50):
|
| 199 |
"""
|
| 200 |
計算成交量分佈圖 (Volume Profile) 的數據。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 201 |
"""
|
| 202 |
if df.empty or 'High' not in df.columns or 'Low' not in df.columns or 'Volume' not in df.columns:
|
| 203 |
return None, None, None
|
| 204 |
|
|
|
|
| 205 |
all_prices = np.concatenate([df['High'].values, df['Low'].values])
|
| 206 |
min_price = all_prices.min()
|
| 207 |
max_price = all_prices.max()
|
|
|
|
| 209 |
price_for_volume = (df['High'] + df['Low'] + df['Close']) / 3
|
| 210 |
df_vol_profile = df.copy()
|
| 211 |
df_vol_profile['Price_Indicator'] = price_for_volume
|
| 212 |
+
df_vol_profile['Volume'] = df_vol_profile['Volume']
|
| 213 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 214 |
hist, bin_edges = np.histogram(df_vol_profile['Price_Indicator'], bins=num_bins, range=(min_price, max_price), weights=df_vol_profile['Volume'])
|
|
|
|
|
|
|
| 215 |
price_centers = (bin_edges[:-1] + bin_edges[1:]) / 2
|
| 216 |
|
| 217 |
return bin_edges, hist, price_centers
|
|
|
|
| 1122 |
|
| 1123 |
|
| 1124 |
# 技術面分析
|
|
|
|
| 1125 |
technical_text = html.Div([
|
| 1126 |
html.P([
|
| 1127 |
html.Strong("價格趨勢:"),
|
|
|
|
| 1210 |
])
|
| 1211 |
|
| 1212 |
# 市場展望
|
|
|
|
| 1213 |
if price_change > 10:
|
| 1214 |
outlook_tone = "謹慎樂觀"
|
| 1215 |
outlook_color = "#dc3545"
|
|
|
|
| 1265 |
return fig
|
| 1266 |
|
| 1267 |
# 定義PMI顏色 (50以上擴張,以下緊縮)
|
|
|
|
| 1268 |
def get_pmi_color(value):
|
| 1269 |
return 'red' if value >= 50 else 'green'
|
| 1270 |
|
|
|
|
| 1289 |
fig.add_hline(y=50, line_dash="dash", line_color="black", annotation_text="榮枯線(50)")
|
| 1290 |
|
| 1291 |
# 添加背景色區域
|
|
|
|
| 1292 |
fig.add_hrect(
|
| 1293 |
y0=50, y1=60,
|
| 1294 |
fillcolor="lightcoral", opacity=0.2,
|
|
|
|
| 1380 |
if comparison_data:
|
| 1381 |
table_rows = []
|
| 1382 |
for item in sorted(comparison_data, key=lambda x: x['return'], reverse=True):
|
|
|
|
| 1383 |
color = 'red' if item['return'] > 0 else 'green'
|
| 1384 |
table_rows.append(
|
| 1385 |
html.Tr([
|
|
|
|
| 1421 |
sentiment_score = np.random.uniform(30, 80) # 模擬情緒分數 0-100
|
| 1422 |
|
| 1423 |
# 建立情緒指標圓形圖
|
|
|
|
| 1424 |
gauge_fig = go.Figure(go.Indicator(
|
| 1425 |
mode = "gauge+number+delta",
|
| 1426 |
value = sentiment_score,
|