[筆記]動態時間規整(Dynamic Time warping)比較ETF相似性
找出走勢相近資產
最近開始在看Artificial Intelligence in Finance(ISBN: 9781492055433),本來打算在Medium上邊看邊寫個筆記整理,但突然想起還沒好好整理過Dynamic time warping相關的東西。
為防止之後又忘記,決定先寫一篇做個記錄,閱讀筆記後續再處理。
動態時間規整(Dynamic Time warping)原理
動態時間規整是衡量兩個序列相似度的方法,但特點在於動態時間規整並不限制在單點對應單點,即一序列的某一時間上的點有機會對多另一序列的多個點,以同一時段的TSM股價與TSM 股價lag1當例子。
由於時間滯後了一期,所以整體序列價格直接錯開,以下再利用DTW矩陣尋找對應點,計算距離以(X-Y)²計算
其中標黃的部分為DTW的最佳對應點,可以發現中間的點均自動對齊了滯後一期前的價格,在中間各點完整對應下只剩下從期初產生的偏離0.3599988,到最後再加上期末的偏離0.88360564即為1.24360444,開根號為1.115即為DTW偏離度,若要以Excel實現可參考以下Excel公式。
以下算法代碼均修改自tslearn套件之源碼
import numpy as np
import yfinance as yf
import pandas as pd
from tslearn.metrics import dtw
stock=yf.download("TSM",start="2023-12-08",end='2023-12-22')['Close']
stock=stock.reset_index()
stock['TSM lag1']=stock['Close'].shift(1)
stock=stock[(stock['Date']>=pd.to_datetime(20231211,format='%Y%m%d'))&(stock['Date']<=pd.to_datetime(20231219,format='%Y%m%d'))]
stock.columns=['Date','TSM','TSM lag1']
series1=np.array(list(stock['TSM']))
series2=np.array(list(stock['TSM lag1']))
mask=np.zeros((series1.shape[0],series2.shape[0]))
l1 = series1.shape[0]
l2 = series2.shape[0]
cum_sum = np.full((l1 + 1, l2 + 1), np.inf)
cum_sum[0, 0] = 0.0
for i in range(l1):
for j in range(l2):
if np.isfinite(mask[i, j]):
cum_sum[i + 1, j + 1] = (series1[i]-series2[j])**2
cum_sum[i + 1, j + 1] += min(
cum_sum[i, j + 1], cum_sum[i + 1, j], cum_sum[i, j]
)
cum_sum=cum_sum[1:, 1:]
#與Package值符合
print(cum_sum[6,6]**(0.5)==dtw(series1,series2))
實例測試
本次利用台股ETF最近21天還原淨值累積報酬率進行DTW測試,基準標的使用0056元大高股息,找出與其21天漲跌幅走勢最接近之3檔ETF,結果與走勢如下,其中00731、00881、00878均與0056走勢高度相似。
import numpy as np
import pandas as pd
from tslearn.metrics import dtw
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei']
plt.rcParams['axes.unicode_minus'] = False
def plot_columns(df, columns):
for col in columns:
if col not in df.columns:
raise ValueError(f"Column '{col}' not found in DataFrame")
ax=df[columns].plot(figsize=(10, 6), title="至2024/1/10 還原淨值 21天累積報酬率")
plt.xlabel("Date")
plt.ylabel("Value")
y_offset=0.95
y_space=0.05
for col in columns:
final_value=((df[col].iloc[-1])).round(3)
plt.text(0.05,y_offset,f"{col}:{final_value}",transform=ax.transAxes)
y_offset-=y_space
plt.show()
data=pd.read_excel("data.xlsx")
data=data.set_index('日期')
#轉換為累計報酬(%)
data=(data/data.iloc[0,:])-1
data=data*100
target='0056_元大高股息'
other=[i for i in data.columns if i!=target]
#計算所有台股ETF與0056之dtw
etf_ticker=[]
dtw_list=[]
for i in other:
etf_ticker.append(i)
dtw_list.append(dtw(data[target],data[i]))
dtwtab=pd.DataFrame(zip(etf_ticker,dtw_list),columns=['比較之ETF','DTW偏離度'])
dtwtab['基準ETF']=target
dtwtab=dtwtab.sort_values('DTW偏離度',ascending=True)
plot_columns(data, [target]+list(dtwtab['比較之ETF'][0:3]))
多維度DTW
tslearn dtw()同時亦提供多維DTW的計算,但在計算上同樣以(x-y)²計算,只是從單一點距離變成了不同維度的距離累加,以下代碼引用自https://github.com/tslearn-team/tslearn,有興趣可自行研究。
(上文內容為個人測試用,僅供參考)