iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 4
1
AI & Data

量化交易30天系列 第 4

量化交易30天 Day4 - 運用K線型態來交易

  • 分享至 

  • xImage
  •  

量化交易30天
本系列文章是紀錄一位量化交易新手的學習過程,除了基礎的Python語法不說明,其他金融相關的東西都會一步步地說明,希望讓更多想學習量化交易但是沒有學過相關金融知識的朋友們,透過這系列的文章,能夠對量化交易略知一二,也歡迎量化交易的高手們多多交流。

還記得Day3的最後,成功的用Python抓到Evening Star出現的日期嗎?複習一下K線圖如下:

夜星的K線型態,通常代表股價趨勢轉弱,預期未來股價下跌的機率比較高,所以現在就要來回測看看,到底這個結論是不是真的。

Evening Star出現後的報酬率

  1. 先抓取SPY股價資料

import os
import pandas_datareader as pdr
SPY = pdr.get_data_tiingo('SPY', api_key='YOUR API KEY')
SPY = SPY.reset_index(level=[0,1])
SPY.index = SPY['date']
SPY_adj = SPY.iloc[:,7:11]
SPY_adj.columns = ['Close','High','Low','Open']
  1. 計算報酬率

關於計算報酬率的方法,舉個範例會比較好理解,假設2016/09/23這天出現訊號(代表這天是Evening star的第3根K棒),我們只能在下1個交易日(2016/09/26)的開盤開始交易,假設9/26買進、9/27賣出的話(隔1天賣),報酬率就會是"9/27的開盤價÷9/26的開盤價",也就是下面程式碼中的ret1;如果9/26買進,過5日後賣,就是下面的ret5。

# 使用開盤價計算報酬率
SPY_Open_adj = SPY_adj.Open
SPY_Open_adj
---------------
date
2015-09-03 00:00:00+00:00    177.578005
2015-09-04 00:00:00+00:00    174.492603
2015-09-08 00:00:00+00:00    177.288466
2015-09-09 00:00:00+00:00    180.346724
2015-09-10 00:00:00+00:00    176.039828
                                ...    
2020-08-25 00:00:00+00:00    343.530000
2020-08-26 00:00:00+00:00    344.760000
2020-08-27 00:00:00+00:00    348.510000
2020-08-28 00:00:00+00:00    349.440000
2020-08-31 00:00:00+00:00    350.350000
Name: Open, Length: 1257, dtype: float64
---------------

# 看到訊號後隔天交易,買完隔1日賣/買完隔5日賣 的報酬率
ret1 = SPY_Open_adj.shift(-2) / SPY_Open_adj.shift(-1)
ret1
-------
date
2015-09-03 00:00:00+00:00    1.016023
2015-09-04 00:00:00+00:00    1.017250
2015-09-08 00:00:00+00:00    0.976119
2015-09-09 00:00:00+00:00    1.004215
2015-09-10 00:00:00+00:00    1.008036
                               ...   
2020-08-25 00:00:00+00:00    1.010877
2020-08-26 00:00:00+00:00    1.002669
2020-08-27 00:00:00+00:00    1.002604
2020-08-28 00:00:00+00:00         NaN
2020-08-31 00:00:00+00:00         NaN
Name: Open, Length: 1257, dtype: float64
-------

ret5 = SPY_Open_adj.shift(-6) / SPY_Open_adj.shift(-1)
ret5
-------
date
2015-09-03 00:00:00+00:00    1.021260
2015-09-04 00:00:00+00:00    1.003419
2015-09-08 00:00:00+00:00    0.997491
2015-09-09 00:00:00+00:00    1.028063
2015-09-10 00:00:00+00:00    1.006988
                               ...   
2020-08-25 00:00:00+00:00         NaN
2020-08-26 00:00:00+00:00         NaN
2020-08-27 00:00:00+00:00         NaN
2020-08-28 00:00:00+00:00         NaN
2020-08-31 00:00:00+00:00         NaN
Name: Open, Length: 1257, dtype: float64

提醒一下,上面ret1與ret5的數字,因為我是採用收盤價相除的方式計算,所以數字大於1代表正報酬,小於1則相反,NaN則是因為尾端資料不足無法計算。

  1. Evening Star訊號函數

有了報酬率的Series之後,我們只要抓出訊號出現的日期,就可以對照訊號出現後的報酬率是多少了。下面我將Day3抓取K線型態的部分,寫成一個函數:

def Evening_Star_Sig(data):
    # 開盤價/收盤價
    data_Open = data.Open
    data_Close = data.Close
    
    # 當日漲跌
    data_DailyChg = data_Close - data_Open
    
    # 取得每日的振幅
    data_Abs_DailyChg = abs(data_DailyChg)
    
    # 計算統計數據
    mean = data_Abs_DailyChg.mean()
    first_quar = data_Abs_DailyChg.quantile(q=0.25)
    
    # 抓取 第1根大振幅陽線、第2根小振幅陽線或陰線、第3根陰線且振幅大於第1根的1/2
    evening_condition_1 = [0,0]
    for i in range(2, len(data_DailyChg)):
        if ( data_DailyChg[i-2] > mean ) & ( abs(data_DailyChg[i-1]) < first_quar ) & ( data_DailyChg[i] < -0.5*mean ):
            evening_condition_1.append(1)
        else:
            evening_condition_1.append(0)
            
    # 第2根的開盤與收盤價 均大於 第1根的收盤與第3根的開盤
    evening_condition_2 = [0,0]
    for i in range(2, len(data_Open)):
        if ( data_Open[i-1] > data_Close[i-2] ) & ( data_Open[i-1] > data_Open[i] ) & ( data_Close[i-1] > data_Close[i-2] ) & ( data_Close[i-1] > data_Open[i] ):
            evening_condition_2.append(1)
        else:
            evening_condition_2.append(0)
            
    # Evening Star Signal
    evening_star_signal = []
    for i in range(len(evening_condition_1)):
        if ( evening_condition_1[i] == 1 ) & ( evening_condition_2[i] == 1 ):
            evening_star_signal.append(1)
        else:
            evening_star_signal.append(0)
            
    # Return a boolean series
    import pandas as pd
    sig = pd.Series(index = data.index, data = evening_star_signal)
    sig = sig.astype('bool')
    return sig

這個函數只需要帶入OHLC時間序列的資料,就可以抓出訊號出現的日期,把SPY的資料帶入看看吧:

# 抓取 Evening star訊號
sig = Evening_Star_Sig(SPY_adj)
sig
------
date
2015-09-03 00:00:00+00:00    False
2015-09-04 00:00:00+00:00    False
2015-09-08 00:00:00+00:00    False
2015-09-09 00:00:00+00:00    False
2015-09-10 00:00:00+00:00    False
                             ...  
2020-08-25 00:00:00+00:00    False
2020-08-26 00:00:00+00:00    False
2020-08-27 00:00:00+00:00    False
2020-08-28 00:00:00+00:00    False
2020-08-31 00:00:00+00:00    False
Length: 1257, dtype: bool
------

sig是一個time series,如果當天有出現訊號的話,value會是True,否則為False。

  1. 對照出現訊號後的報酬率

如果我們想在訊號出現後,做隔日沖的交易,那麼就可以使用ret1:

從下面資料可以看出,在2015/9/3~2020/8/31中間,出現5次訊號,隔日沖的報酬率則是沒有明顯特徵。

ret1[sig]
------
date
2016-09-23 00:00:00+00:00    0.995489
2018-04-06 00:00:00+00:00    1.011095
2018-06-21 00:00:00+00:00    0.991947
2018-10-18 00:00:00+00:00    0.999531
2019-08-20 00:00:00+00:00    1.002564
Name: Open, dtype: float64
------

使用ret5來計算,可以觀察到,如果隔5天賣的話,可能會遇到下跌較多的情形。

ret5[sig]
------
date
2016-09-23 00:00:00+00:00    1.003721
2018-04-06 00:00:00+00:00    1.021540
2018-06-21 00:00:00+00:00    0.987158
2018-10-18 00:00:00+00:00    0.959550
2019-08-20 00:00:00+00:00    0.978323
Name: Open, dtype: float64
------
  1. 畫平均報酬率圖

如果我們想要計算訊號出現後買賣的平均報酬率,計算方式非常簡單,只要加個mean即可:

ret1[sig].mean()
# 1.0001251917245995

ret5[sig].mean()
# 0.990058399305416

這個平均報酬率,就可以用來判斷,夜星出現後,報酬率最好/最差的買賣天數是多少。因此我就把平均報酬率對應天數的圖表畫出來看看:

# 回測 Evening star出現後,買賣間隔1~100天的平均報酬率
rets = []
for i in range(2,102):
    ret = SPY_Open_adj.shift(-i) / SPY_Open_adj.shift(-1)
    rets.append(ret[sig].mean())
    
# 畫出天數對應報酬率的圖
import pandas as pd
import matplotlib.pyplot as plt
ret_df = pd.DataFrame(index=range(1,101),data=rets)
ret_df.columns = ['return']
ret_df = (ret_df-1) * 100
plt.figure(figsize=(12,8))
plt.plot(ret_df)
plt.hlines(y=0, xmin=0, xmax=100, color='red')
plt.title("平均報酬率 v.s. 買賣間隔時間",fontsize=15)
plt.xlabel("買賣間隔天數(天)", fontsize=15)
plt.ylabel("平均報酬率(%)", fontsize=15)

從這張圖可以發現,在2015/9/3~2020/8/31中間,出現的5次訊號來做回測,只有1天到5天內買賣的平均報酬率是負的(紅線以下),剩下的報酬率都是正的,跟一開始預期的結果剛好是相反的,一個看壞的訊號卻得到不錯的報酬率,很酷吧!

推測主要原因是因為SPY是追蹤標普500指數的一檔商品,在回測的這段期間內,標普500指數的趨勢是上漲的,因此只要買賣間隔時間長一點,基本上都可以得到不錯的報酬率,但是在短期的股價上可能有一些下跌的機會,所以它可能比較適合用來當作短線操作的交易訊號。

本篇總結
本篇介紹了K線型態如何做交易,下一篇會開始介紹RSI相對強弱指標,請繼續收看~~

P.S.
如果大家對於量化交易有興趣的話,我自己有上過以下這門課,課程內容從串接股市資料API、儲存至資料庫、將自己的策略轉化成程式碼、自動下單,並且可以把整個流程自動化,每天早上執行一次,一整天就不用看盤了,覺得是蠻實戰的,可以參考看看。

筆者 Sean
奈米戶投資人 / Python愛用者
喜歡用Python玩轉金融數據,從個股基本面、技術面、籌碼面相關資料,一直到總體經濟數據,都是平常接觸到的素材;對於投資,除了研究歷史數據,也喜歡瞭解市場上大家在玩些什麼。


上一篇
量化交易30天 Day3 - 分析K線型態
下一篇
量化交易30天 Day5 - Python計算RSI
系列文
量化交易30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

1
ckchuang
iT邦新手 4 級 ‧ 2020-09-17 16:21:37

好喜歡這系列,很淺顯易懂的範例

另外想請教大大,若是將這個訊號拿去回測趨勢向下的歷史數據,像是科技泡沫時期或金融海嘯,是否就會得到相反的結果呢?

Sean Liu iT邦新手 4 級 ‧ 2020-09-18 10:57:25 檢舉

Hi~~很高興你喜歡這個系列
其實我也沒有拿這個訊號去回測空頭的時間,可以找個時間來玩玩看,感覺會有不同的結論XD

我要留言

立即登入留言