2025年1月20日 星期一

Python 學習筆記 : 如何在 K 線圖上標記買賣信號

在之前的量化投資測試中, 使用了基於 mplfinance 的 kbar.py 模組來簡化 K 線圖之繪製, 但只是用 mav 參數或自行計算之 SMA 指標在 K 線圖上疊圖而已, 本篇要來測試如何在 K 線圖上打上標記, 例如在最常見的雙均線交叉策略中於黃金交叉點打上一個向上三角形 ▲ 標示買點; 而在死亡交叉處打上向下三角形 ▼ 標示賣點. 

本系列之前的文章參考 : 


K 線圖繪製模組 kbar.py 原始碼如下 :

# kbar.py
import mplfinance as mpf

class KBar():
    def __init__(self, df):
        self.df=df
        self.addplots=[]
    def addplot(self, data, **kwargs):
        plot=mpf.make_addplot(data, **kwargs)
        self.addplots.append(plot)
    def plot(self, embedding=False, **kwargs):
        color=mpf.make_marketcolors(up='red', down='green', inherit=True)   
        font={'font.family': 'Microsoft JhengHei'}   
        style=mpf.make_mpf_style(base_mpf_style='default',
                                 marketcolors=color,
                                 rc=font)
        kwargs['type']='candle'
        kwargs['style']=style
        kwargs['addplot']=self.addplots
        if 'returnfig' in kwargs and kwargs['returnfig'] is True:
            fig, ax=mpf.plot(self.df, **kwargs)
            return fig
        else:
            mpf.plot(self.df, **kwargs)

首先匯入本篇測試要用到的模組 : 

>>> import kbar    
>>> import yfinance as yf   
>>> import ta   

從 yfinance 下載價量資料 :

>>> df=yf.download('0050.TW', start='2024-10-20', end='2025-01-20')   
[*********************100%%**********************]  1 of 1 completed   

用 ta 套件計算長短天期 SMA 指標, 長短天期是相對的概念, 跨度因人而異, 此處取 5 日 (周線) 為短天期, 20 日 (月線) 為長天期. 

呼叫 ta 的 SMA 指標類別建構式 SMAIndicator() 建立指標物件 : 

>>> SMA5=ta.trend.SMAIndicator(df['Close'], window=5)      # 短天期均線
>>> SMA20=ta.trend.SMAIndicator(df['Close'], window=20)   # 短天期均線

呼叫指標物件之 sma_indicator() 方法計算 SMA 指標 : 

>>> sma5=SMA5.sma_indicator()   
>>> sma20=SMA20.sma_indicator()    

然後建立 KBar 物件來繪製 K 線圖 : 

>>> kb=kbar.KBar(df)   

呼叫 KBar 物件的 addplot() 方法建立疊圖 (預設 panel=0 為疊圖)

>>> kb.addplot(sma5, color='blue', width=1)       # 疊圖 1
>>> kb.addplot(sma20, color='black', width=1)   # 疊圖 2

最後呼叫 KBar 物件的 plot() 方法繪製 K 線圖 :

>>> kb.plot(volume=True)    # 顯示成交量

結果如下 :




此雙均線交易策略顯示在過去三個月中至少出現一次黃金交叉的買入訊號與一次死亡交叉的賣出信號, 我們的目標是要在此 K 線圖上用 ▲ 與 ▼ 標示買賣點. 

首先偵測黃金交叉點, 即短天期均線 sma5 向上突破長天期均線 sma20 (大於), 且前一天 shift(1) 尚未向上突破 (小於或等於) :

>>> golden_cross=(sma5 > sma20) & (sma5.shift(1) <= sma20.shift(1))    
>>> golden_cross   
Date
2024-10-21    False
2024-10-22    False
2024-10-23    False
2024-10-24    False
2024-10-25    False
              ...  
2025-01-13    False
2025-01-14    False
2025-01-15    False
2025-01-16    False
2025-01-17    False
Length: 63, dtype: bool

其次偵測死亡交叉點, 即短天期均線 sma5 向下跌破長天期均線 sma20 (小於), 且前一天 shift(1) 尚未跌破 (大於或等於) :

>>> death_cross=(sma5 < sma20) & (sma5.shift(1) >= sma20.shift(1))  
>>> death_cross  
Date
2024-10-21    False
2024-10-22    False
2024-10-23    False
2024-10-24    False
2024-10-25    False
              ...  
2025-01-13    False
2025-01-14    False
2025-01-15     True
2025-01-16    False
2025-01-17    False
Length: 63, dtype: bool

我們用此兩個布林 Series 去選取收盤價 (也可以是開盤價) 會得到一個子 Series : 

>>> df['Close'][golden_cross]   
Date
2024-12-05    196.5
Name: Close, dtype: float64
>>> df['Close'][death_cross]       
Date
2025-01-15    194.100006
Name: Close, dtype: float64

可見符合黃金與死亡交叉條件的交易日都各只有一個, 如果要在這兩點上打上標記, 就必須用一個與 K 線圖等長的 Series 來疊圖, 而此 Series 僅在上面兩處有值, 其餘日期為 NaN, 方法是將這兩個子 Series 傳給 pd.Series() 且傳入 index=df.index 確保索引與 df 一致, 這樣就可以將這兩個子 Series 拉長到與 df 一樣長才能疊圖 : 

>>> golden_cross_points=pd.Series(df['Close'][golden_cross], index=df.index)   
>>> death_cross_points=pd.Series(df['Close'][death_cross], index=df.index)       

檢視拉長後的 Series : 

>>> golden_cross_points   
Date
2024-10-21   NaN
2024-10-22   NaN
2024-10-23   NaN
2024-10-24   NaN
2024-10-25   NaN
              ..
2025-01-13   NaN
2025-01-14   NaN
2025-01-15   NaN
2025-01-16   NaN
2025-01-17   NaN
Name: Close, Length: 63, dtype: float64
>>> death_cross_points   
Date
2024-10-21           NaN
2024-10-22           NaN
2024-10-23           NaN
2024-10-24           NaN
2024-10-25           NaN
                 ...    
2025-01-13           NaN
2025-01-14           NaN
2025-01-15    194.100006
2025-01-16           NaN
2025-01-17           NaN
Name: Close, Length: 63, dtype: float64

這樣就可以用這兩個 Series 來繪製疊圖以標記買賣點了 : 

>>> kb.addplot(golden_cross_points, scatter=True, markersize=50, marker='^', color='blue', label='Golden Cross')   
>>> kb.addplot(death_cross_points, scatter=True, markersize=50, marker='v', color='black', label='Death Cross')   
>>> kb.plot(volume=True)    

結果如下 :




不過從這張圖來看, 此策略在此似乎是虧錢啊! 以月線當長天期反應時間較慢, 等賣出信號出現股價已經跌一段了.  

沒有留言 :