技術分析簡介與回測

photo by Unsplash

前言

市場上大多數投資人使用的投資方式主要分成技術面、基本面還有籌碼面,其中使用到歷史股價資料最多的就是技術面的分析。因此,我們本周透過Python對投資人較常使用的數個技術分析指標進行程式教學並在最後進行回測應用。

本文重點概要

  • 技術分析/指標介紹
  • 回測應用

技術分析簡介

技術分析主要透過過去的資訊(歷史價量)來解釋未來走勢或決定投資的時機點位。但當然地,過去並不能完全代表未來,但對於想要將市場現況進行一個量化的了解時,技術分析就可以提供一些指標供投資人簡化過去的資訊以做參考噢~

本篇並不會著重在技術分析的優劣比較主要以透過程式碼的方式讓大家對於一般常用的技術指標有了更進一步的認識,了解到看盤軟體中這些數值的誕生,並可以將程式碼運用在其他的地方或者指標上~

技術分析例子:均線MA、KD指標、RSI指標、MACD等

技術指標介紹

正式開始介紹前,仍要先把資料從我們的TEJ API撈取下來,本次使用的資料是聯電(2303)過去一年的歷史資料。

import tejapi 
import pandas as pd
import numpy as np
import datetime
tejapi.ApiConfig.api_key = "your key"
UMC = tejapi.get( 
    'TWN/EWPRCD', 
    coid = '2303',
    mdate={'gte':'2020-04-01', 'lte':'2021-04-25'},
    opts={'columns': ['mdate','open_d','high_d','low_d','close_d',              'volume']}, 
    paginate=True)
UMC = UMC.set_index('mdate')
聯電(2303)歷史資料

在有了歷史資料後,就正式進入我們本周的技術指標內容拉~~

MACD

MACD是透過比較兩條長(26)短(12)天數的指數移動平均線(EMA),相減算出差值(DIF)後,對差值再進行一次指數移動平均線的計算。用比較白話一點的說法就是,透過長短天數的均線之間的交錯裝況對標的進行買賣時機的判斷。

EMA: 跟一般簡單移動平均不同的地方是,越接近當期的資料重要性越高,也因此佔比越高。
DIF(短): 短期跟長期EMA相減,通常代表短期股價趨勢變化。
MACD(長): 對DIF再取一次移動平均,通常用以判斷股價大趨勢。

def MACD(DF, a = 12, b =26, c =9):
    '''
    簡單移動平均線: rolling
    指數移動平均線: ewm
    '''
    df = DF.copy()
    df["EMA_Fast"] = 
         df['open_d'].ewm(span = a, min_periods = a).mean()
    df["EMA_Slow"] = 
         df['open_d'].ewm(span = b, min_periods = b).mean()
    df["DIF"] = df["EMA_Fast"] - df["EMA_Slow"]
    df["MACD"] = df['DIF'].ewm(span = c, min_periods = c).mean()
    
    return df

布林通道(Bollinger Bands)

布林通道主要是透過移動平均線、上線(上標準差)以及下線(下標準差)三條線組合而成,顯示出股價的安全區域。

用兩個標準差的原因是,在統計學常態分配當中,95%的機率會落在兩個標準差之間,雖然說股價不完全符合常態分配,但仍有大部分時間會落在這個上下兩個標準差構成的通道之間。

def BollingerBand(DF, n=20):
    '''
    標準差: std
    '''
    df = DF.copy()
    df['SMA'] = df['close_d'].rolling(window = n).mean()
    df['BB_20dstd_up'] = 
         df['SMA'] + 2*df['close_d'].rolling(window = n).std(ddof=0)
    df['BB_20dstd_down'] = 
         df['SMA'] - 2*df['close_d'].rolling(window = n).std(ddof=0)
    df['BB_width'] = df['BB_20dstd_up'] - df['BB_20dstd_down']
    df.dropna(inplace = True)
    
    return df

RSI指標

RSI(相對強弱指標)主要用在評估股市當中”買賣雙方力道的強弱”,透過近期價格變化幅度來評估說標的是超買/賣的情況。

計算上主要透過一段時間內(本文使用14天)
股價平均漲幅:(上漲天數/累積漲幅)及
股價平均跌幅:(下跌天數/累積跌幅)去算出
RSI:平均漲幅/(平均漲幅+平均跌幅)

RSI數值在0~100之內,數值越大或越小就表示力道越強,通常以30表示超賣(賣太多)、70表示超買(買太多),也就是透過這個數值去決定說是否要進行買入或者賣出的動作

def RSI(DF, n=14):
    '''
    n日漲幅平均值 = n日內上漲日總上漲幅度加總 ÷ n
    n日跌幅平均值 = n日內下跌日總下跌幅度加總 ÷ n
    '''
    df = DF.copy()
    df['daliy_change'] = 
         df['close_d'] - df['close_d'].shift(1)
    df['dUp'] = 
         np.where(df['daliy_change'] >= 0, df['daliy_change'], 0)
    df['dDown'] = 
         np.where(df['daliy_change'] < 0, -df['daliy_change'], 0)
    avg_dUp = []
    avg_dDown = []
    dUp = df['dUp'].tolist()
    dDown = df['dDown'].tolist()
    
    for i in range(len(df)):
        if i < n:
            avg_dUp.append(0)
            avg_dDown.append(0)
        elif i == n:
            avg_dUp.append(df['dUp'].ewm(span = n).mean()[n])
            avg_dDown.append(df['dDown'].ewm(span = n).mean()[n])
        else:
            avg_dUp.append(((n-1)*avg_dUp[i-1] + dUp[i])/n)
            avg_dDown.append(((n-1)*avg_dDown[i-1] + dDown[i])/n)
    
    df['avg_dUp'] = np.array(avg_dUp)
    df['avg_dDown'] = np.array(avg_dDown)
    df['RS'] = df['avg_dUp']/df['avg_dDown']
    df['RSI'] = df['RS'].apply(lambda x: x/(1+x) * 100)
        
    return df

ATR

ATR(平均真實區域)主要指股價在一段時間內(本文使用14天)的真實波動範圍,也就是衡量該標的物之波動性。

計算上若要算出ATR,首先要算出TR,而TR指的是在以下三個數字中最大的那個數字:
1. 今日最高價減最低價
2. 今日最高價減昨日收盤價的絕對值
3. 今日最低價減昨日收盤價的絕對值

因為ATR是衡量波動性的指標,所以可透過在ATR數值很高的時候也就表示近期上下震盪較大,所以可以在此時設想出場時機。

def ATR(DF, n=14):
    '''
    昨日收盤:close.shift()
    '''
    df = DF.copy()
    df['H-L'] = abs(df['high_d'] - df['low_d'])
    df['H-PC'] = abs(df['high_d'] - df['close_d'].shift())
    df['L-PC'] = abs(df['low_d'] - df['close_d'].shift())
    df['TR'] = df[['H-L', 'H-PC', 'L-PC']].max(axis =1, skipna =False)
    df['ATR'] = df['TR'].ewm(span =n, min_periods=n).mean()
    
    return df

KD指標

KD指標,主要用來呈現過去一段時間的標的物價格強弱趨勢,透過這K、D兩個數值去找出可能的價格轉折點作為交易訊號。KD指標與RSI其實概念類似,但不同之處KD指標是看最新的價格他是在相對高點或者低點,而RSI是在比較買賣盤強弱的比例。

計算上主要透過一段時間內(本文使用14天)
RSV:(今日Close – 14天內LOW)/(14天內High -14天內Low) * 100
K值:昨日K*(2/3)+今日RSV*(1/3)
D值:昨日D*(2/3)+今日K*(1/3)

一些投資人所說的黃金/死亡交叉其實就是KD值的應用,K由下往上突破D時就是短期內可能上漲,相反地K由上往下跌破D時就是短期內可能下跌的徵兆。

def KD(DF, n = 14):
    '''
    function to calculate KD
    '''
    df = DF.copy()
    df['High_14D'] = df['high_d'].rolling(n).max()
    df['Low_14D'] = df['low_d'].rolling(n).min()
    df['RSV'] = 
        (df['close_d'] - df['Low_14D']) / (df['High_14D'] - df['Low_14D']) * 100
    df = df.dropna()    
    df['K'] = np.zeros(len(df))
    df['D'] = np.zeros(len(df))
    
    for i in range(len(df)):
        if i == 0:
            df['K'][i] = 50
            df['D'][i] = 50
        else:
            df['K'][i] = df['K'][i-1]*(2/3) + df['RSV'][i]*(1/3)
            df['D'][i] = df['D'][i-1]*(2/3) + df['K'][i]*(1/3)
    
    return df

技術指標回測應用

小編在這邊用幾個簡單的指標當作我們的訊號,但訊號是沒有經過嚴格的測試直接發想的,目的只是要結合上述技術指標跟大家做個介紹,所以即使結果很好大家也盡量不要直接拿來使用嘿!

交易策略設計

買入信號:
MACD上升 且 K值>70 且 當日K值>前日K值
賣出信號:
當日低點<前日收盤 – 前日 ATR

交易假設


買點成立時,我們投入1元新台幣的金額量進行買入直到賣點出現,過程忽略交易手續費、證交稅。

print("Calculating returns for ","UMC")
ret = []
for i in range(1,len(UMC)):
    buy_signal = UMC["DIF"][i]> UMC["MACD"][i] and 
                 UMC["K"][i]> 70 and 
                 UMC["K"][i] > UMC["K"][i-1]
  sell_signal = 
     UMC["low_d"][i] < UMC["close_d"][i-1] - UMC["ATR"][i-1]
  if tickers_signal["UMC"] == "":
        tickers_ret["UMC"].append(0)
        if buy_signal:
            tickers_signal["UMC"] = "Buy"
  elif tickers_signal["UMC"] == "Buy":
        if sell_signal:
            tickers_signal["UMC"] = ""
            tickers_ret["UMC"].append(
    ((UMC["close_d"][i] - UMC["close_d"][i-1])/UMC["close_d"][i-1]))
        else:
            tickers_ret["UMC"].append(
      (UMC["close_d"][i]/UMC["close_d"][i-1])-1)
UMC['ret'] = np.array(tickers_ret["UMC"])

找出買賣點、報酬率之後,就來簡單做個圖看一下這個策略表現如何吧 !

strategy_df = pd.DataFrame()
strategy_df["UMC"] = UMC['ret']
strategy_df["ret"] = strategy_df.mean(axis=1)
(1+strategy_df["ret"]).cumprod().plot()

在過程中表現還算可以接受,當然我們這個只是一項簡易的回測,忽略許多可能的費用,但是確實可以體現到說,如果善加應用技術面指標,確實可以讓投資人簡化許多資訊,並有機會透過策略改良而獲利。

結語

技術指標就跟基本面、籌碼面資料一樣,同樣的數據每個人看法可能不盡相同,但都可以成為投資人中判斷是否進行買賣的工具。本篇文章主要以教導大家如何透過Python自己製作這些技術指標,當然市面上已經有許多工具包將這些指標都寫好了,但自己設計的東西總是有它的好處在,除了練習程式還能了解指標當初設計的原理!!

我們之後也會持續分享以回測為主題的文章,如果讀者們有甚麼想要回測看看的策略也可以跟我們說~最後,如果喜歡本篇文章的內容請幫我們點擊下方圖示 ,給予我們更多支持與鼓勵,有任何的問題都歡迎在下方留言/來信,我們會盡快回覆大家

想要一個"穩定""品質高""資料長度長"的資料源該怎麼辦呢?TEJ API就是你最好的選擇!!

完整程式碼

延伸閱讀

相關網站連結

有任何使用上的問題都歡迎與我們聯繫:聯絡資訊

返回总览页