你是一位資深的網站工程師請幫我製作一個番茄鐘計時器網頁程式詳細需求如下:
## 核心功能:1. 預設 25 分鐘工作時間 2. 大大的倒數計時顯示(分:秒格式) 3. 開始/暫停按鈕 4. 要有一個重置按鈕 5. 時間到時播放提示音並彈出通知「休息一下吧!」 6. 時間走完後自動切換到 5 分鐘休息時間 7. 顯示目前狀態(專注中/休息中)
## 進階功能: 1. 記錄今天完成了幾個番茄鐘 2. 可以自訂工作時間和休息時間 3. 每完成 4 個番茄鐘,休息時間變成 15 分鐘
## 介面設計: 1. 極簡風格,倒數計時數字要超大 2. 工作時間用紅色主題 3. 休息時間用綠色主題 4. 按鈕要大而清楚 5. 顯示進度環(圓形進度條) 6. 適合全螢幕專注使用
我們需要 yfinance 來當作我們的「網路爬蟲」去取得股價,以及 pandas 處理數據、matplotlib 畫圖。 (提醒:Colab 預設不支援中文圖表顯示,為了避免出現亂碼,圖表的標籤我們會先使用英文。)
# 在 Colab 執行這行來安裝 yfinance
!pip install yfinance
import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
這裡是教學的核心。讓學生知道 AI 交易不是魔法,而是數學公式(RSV)的累積。
# 1. 取得台積電資料 (股票代號:2330.TW)
print("正在下載台積電股價資料...")
df = yf.download('2330.TW', start='2023-01-01', end='2024-01-01')
# 處理 yfinance 新版的雙層欄位格式
if isinstance(df.columns, pd.MultiIndex):
df.columns = df.columns.get_level_values(0)
# 2. 計算 KD 指標 (標準設定為 9 天)
# 找出過去 9 天內的最高價與最低價
df['9_High'] = df['High'].rolling(window=9).max()
df['9_Low'] = df['Low'].rolling(window=9).min()
# 計算 RSV (未成熟隨機值:今天股價在過去9天高低點間的相對位置)
df['RSV'] = (df['Close'] - df['9_Low']) / (df['9_High'] - df['9_Low']) * 100
# 初始化 K 值與 D 值 (第一天預設為 50)
K_list = [50]
D_list = [50]
# 排除前面沒有完整 9 天資料的日子
df_valid = df.dropna(subset=['RSV']).copy()
# 使用迴圈,一天一天計算最新的 K 與 D
for rsv in df_valid['RSV']:
# 公式:今天的 K = 昨天的 K * (2/3) + 今天的 RSV * (1/3)
K = K_list[-1] * (2/3) + rsv * (1/3)
# 公式:今天的 D = 昨天的 D * (2/3) + 今天的 K * (1/3)
D = D_list[-1] * (2/3) + K * (1/3)
K_list.append(K)
D_list.append(D)
# 將算好的數字放回資料表 (去掉第一天的預設值 50)
df_valid['K'] = K_list[1:]
df_valid['D'] = D_list[1:]
print("KD 指標計算完成!")
數字很難看出趨勢,教導學生如何利用 Python 將數據變成可以一眼看懂的圖表。
# 3. 畫出股價與 KD 圖
# 設定畫布大小,並切分成上下兩塊 (2:1 的比例)
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8), gridspec_kw={'height_ratios': [2, 1]})
# 上半部:繪製股價折線圖
ax1.plot(df_valid.index, df_valid['Close'], label='TSMC Close Price', color='black')
ax1.set_title('TSMC (2330.TW) Stock Price & KD Indicator')
ax1.legend()
ax1.grid(True) # 加上網格線
# 下半部:繪製 KD 指標
ax2.plot(df_valid.index, df_valid['K'], label='K Line', color='orange')
ax2.plot(df_valid.index, df_valid['D'], label='D Line', color='blue')
# 畫出 80 (超買區) 與 20 (超賣區) 的參考線
ax2.axhline(80, color='red', linestyle='--', alpha=0.5)
ax2.axhline(20, color='green', linestyle='--', alpha=0.5)
ax2.set_ylabel('KD Value')
ax2.legend()
ax2.grid(True)
# 顯示圖表
plt.show()
1.計算並標記「黃金交叉」
昨天:K 值 $\le$ D 值(K 線在下)
今天:K 值 $>$ D 值(K 線穿到上面)
當這兩個條件同時成立,就是一個「黃金交叉」。我們可以使用 Python 資料處理神器 pandas 的 .shift(1)(把資料往下推一格,也就是「昨天」的資料)來輕鬆完成。
# 建立一個新欄位,預設為沒有訊號 (False)
df_valid['Buy_Signal'] = False
# 條件 1:今天的 K > 今天的 D
cond_today = df_valid['K'] > df_valid['D']
# 條件 2:昨天的 K <= 昨天的 D (使用 shift(1) 取得昨天的數值)
cond_yesterday = df_valid['K'].shift(1) <= df_valid['D'].shift(1)
# 進階過濾:通常 KD 都在低檔(例如 30 以下)交叉,訊號才比較準確
cond_low = df_valid['K'] < 30
# 當這三個條件「同時發生」(&),就把 Buy_Signal 設為 True
df_valid.loc[cond_today & cond_yesterday & cond_low, 'Buy_Signal'] = True
# 印出總共找到幾個買進訊號?
buy_count = df_valid['Buy_Signal'].sum()
print(f"總共找到 {buy_count} 個低檔黃金交叉買點!")
2.將買進訊號畫在圖表上:使用散佈圖 (scatter) 畫出向上箭頭。
我們要把原本畫圖的程式碼稍微升級,把剛剛找到的買點用紅色的向上箭頭(^)標示在股價圖與 KD 圖上。
# 重新設定畫布
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8), gridspec_kw={'height_ratios': [2, 1]})
# --- 上半部:股價折線圖 ---
ax1.plot(df_valid.index, df_valid['Close'], label='TSMC Close Price', color='black', alpha=0.7)
# 抓出發生買進訊號的日期與當天收盤價
buy_dates = df_valid[df_valid['Buy_Signal']].index
buy_prices = df_valid[df_valid['Buy_Signal']]['Close']
# 在股價圖上畫出紅色向上箭頭 (marker='^')
ax1.scatter(buy_dates, buy_prices, marker='^', color='red', s=150, label='Buy Signal', zorder=5)
ax1.set_title('TSMC (2330.TW) Stock Price & KD Golden Cross')
ax1.legend()
ax1.grid(True)
# --- 下半部:KD 指標 ---
ax2.plot(df_valid.index, df_valid['K'], label='K Line', color='orange')
ax2.plot(df_valid.index, df_valid['D'], label='D Line', color='blue')
ax2.axhline(80, color='red', linestyle='--', alpha=0.5)
ax2.axhline(20, color='green', linestyle='--', alpha=0.5)
# 在 KD 圖上也同步標示發生交叉的位置
buy_k_values = df_valid[df_valid['Buy_Signal']]['K']
ax2.scatter(buy_dates, buy_k_values, marker='^', color='red', s=150, zorder=5)
ax2.set_ylabel('KD Value')
ax2.legend()
ax2.grid(True)
plt.show()