【Python 量化投資】使用技術指標建構交易策略

Renk Huang
Ideal Investment | 理想投資
8 min readMay 17, 2021

以台指期為例,使用布林通道來建構交易策略

前言

在財金領域的課程中一定會提到著名的效率市場假說,效率市場假說可分為三種弱式、半強式和強式。

弱式效率市場下強調當前股價以完全反映歷史價格所帶來的資訊,故認為投資人無法透過技術分析賺取超額報酬

半強式市場則認為當前的股價已完全充分地反映所有市場上已經公開的情報,故無法利用基本分析獲得超額報酬

最後,強式效率市場宣稱內線交易會在很短的時間內反應在價格上,因此投資人也無法利用內線消息賺取超額報酬

但真實的世界裡,如果技術分析,基本面分析,甚至是內線消息都無法得到超越大盤的績效,那到底為何投資人仍汲汲營營於這些分析方法呢?

本文章將以布林通道( Bollinger bands)為例,來測試技術分析到底能不能賺到超越大盤的績效!!!

✨本文重點 ✨

  • 布林通道 介紹/應用
  • 建構策略

🚆 布林通道

布林通道共有3條線,分別為上、下界和中線,中線為移動平均線,上下界透過中線的離差倍數求算出:

中線:20天的移動平均線
標準差:20天的收盤價計算出的標準差
上界:中線 +2倍的標準差
下界:中線 - 2倍的標準差

def bollinger_band(price,ndays,numstd):
mid_band = price.rolling(ndays).mean()
std = price.rolling(ndays).std()
upper_band = mid_band+numstd*std
lower_band = mid_band-numstd*std
return mid_band, lower_band, upper_band
# 建立指標
bb = bollinger_band(txf['收盤價'],20,2)
txf['mid_band'],txf['lower_band'],txf['upper_band'] = bb[0]\
,bb[1]\
,bb[2]
txf.dropna().reset_index(drop=True)
資料展示

📈 資料視覺化 📈

# 資料視覺化
plt.style.use('seaborn-darkgrid')
plt.figure(figsize=(20,10))
date = txf['日期']
plt.plot(date,txf['mid_band'],color = 'orange',label ='mid_band' )
plt.plot(date,txf['lower_band'],color = 'green',label ='lower_band' )
plt.plot(date,txf['upper_band'],color = 'green',label ='upper_band' )
plt.plot(date,txf['收盤價'],color = 'blue',label ='close_price' )
plt.legend(fontsize = 15)
資料視覺化

🔽 建構策略 🔽

當價格突破上界時放空,反之當價格突破下界時買進。

cross_over : 當日收盤突破上界,做空。
cross_under:當日收盤突破下界,做多。
signal:做多信號 1,做空信號 -1。
market_position:對 signal 做累加計算,紀錄當前持倉水位。
由於信號出現於當日收盤價之後,因此建立部位皆是在 t+1日,並以t+1日的開盤價做計算。
initial_equity:初始資金
point_change:每天的收盤與開盤的差距
equity_change:權益變動,market_position ✖️ point_change,故未持倉的情況下就不會計算損益。
equity_change_cumulative:每日權益變動的累積數。

💻績效損益計算

# cross_over & cross_under, signal, market_position #
txf['cross_over'] = np.where(txf['收盤價']>txf['upper_band'],1,0)
txf['cross_under'] = np.where(txf['收盤價']<txf['lower_band'],1,0)
txf['signal'] = np.where((txf['cross_over']>0)&(txf['cross_under']==0),-1,np.nan)
txf['signal'] = np.where((txf['cross_over']==0)&(txf['cross_under']>0),1,txf['signal'])
txf['signal'] = txf['signal'].fillna(0)
# 持倉 #
txf['market_position'] = txf['signal'].cumsum()
txf['market_position'] = txf['market_position'].shift(1)
# 損益試算 #
txf['initial_equity'] = 1000000
txf['point_change'] = txf['收盤價']-txf['開盤價']
txf['equity_change'] = txf['market_position']*txf['point_change']*200
txf['equity_change_cumulative'] = txf['equity_change'].cumsum()
txf['equity'] = txf['initial_equity']+txf['equity_change_cumulative']
# MDD #
roll_max = txf['equity'].cummax()
monthly_dd =txf['equity']/roll_max - 1.0
txf['maxdropdown'] = monthly_dd.cummin()
績效計算

📈 回測績效視覺化 📈

plt.figure(figsize=(20,10))
fig, ax1 = plt.subplots(figsize=(20,10))
plt.yticks(fontsize=15)
date = txf['日期']
ax2 = ax1.twinx()
plt.yticks(fontsize=15)
ax3 = ax1.twinx()
ax3.spines['right'].set_position(('outward', 60))
plt.yticks(fontsize=15)
# set x, y label
ax1.set_xlabel("Date",fontsize=15)
ax1.set_ylabel("Money",fontsize=15)
ax2.set_ylabel("Number of position",fontsize=15)
ax3.set_ylabel("MDD(%)",fontsize=15)
#
ax1.set_xticklabels(labels = date,fontdict = {'fontsize':15})
#
p1, = ax1.plot(date,txf['equity'],color = 'blue',label ='equity') # 第一條線
ax1.grid(True)
p2, = ax2.plot(date,txf['market_position'],color = 'orange',label ='market_position')# 第二條線
ax2.grid(False)
p3, = ax3.plot(date,txf['maxdropdown'],color = 'red',label ='MDD')# 第三條線
ax3.grid(False)
# set legend
lns = [p1, p2, p3]
ax1.legend(handles=lns, loc='best',fontsize = 20)
績效視覺化

📘總結

由上面的策略可看出,儘管在從2010至2015的期間,最終權益數(藍線)為900萬,與起始資金100萬相比,賺了8倍。但同時也可以看到,該策略的最大回檔(紅線)曾突破100%,換句話說,我們使用該策略在賺到900W萬以前就會因為虧損過大而失去所有本金。但這並不表示布林通道不是個好策略,我們只能說,布林通道在(20天, 2被標準差)這組參數下表現不佳。

我們將在下集與大家分享如何挑選有效的參數 😎😎

最後,如果喜歡本篇文章的內容請幫我們點擊下方圖示👏 ,給予我們更多支持與鼓勵,有任何的問題都歡迎在下方留言/來信,我們會盡快回覆大家👍👍

--

--